// Matrix_Dense.h Copyright (C) 2009 adolfo@di-mare.com #ifdef Spanish_dox /** \file Matrix_Dense.h \brief Declaraciones y definiciones para la clase \c Matrix_Dense.<> \author Adolfo Di Mare \date 2009 - Why English names??? ==> http://www.di-mare.com/adolfo/binder/c01.htm#sc04 */ #endif #ifdef English_dox /** \file Matrix_Dense.h \brief Declarations and definitiones for class \c Matrix_Dense<>. \author Adolfo Di Mare \date 2009 - Why English names??? ==> http://www.di-mare.com/adolfo/binder/c01.htm#sc04 */ #endif #ifndef Matrix_Dense_h #define Matrix_Dense_h // Evita la inclusión múltiple#include "Matrix_BASE.h" #include "Matrix_BASE.h" namespace Mx { #ifdef Spanish_dox /// Esta es una clase matriz muy chirrisquitica que puede cambiar dinámicamente de tamaño. #endif #ifdef English_dox /// This is a very chirrisquitica matrix that can change size dinamically. #endif /// \copydetails Matrix_BASE template class Matrix_Dense : public Matrix_BASE { public: Matrix_Dense(unsigned m = 1, unsigned n = 1); Matrix_Dense(const Matrix_Dense& o); /// \copydoc Matrix_BASE::Matrix_BASE(const E&) Matrix_Dense(const E& V) : Matrix_BASE() , m_rows(0), m_cols(0), m_val(0) { reSize(1,1); (*this)(0,0) = V; } ~Matrix_Dense(); ///< Destructor. public: unsigned rows() const { return m_rows; } unsigned cols() const { return m_cols; } unsigned size() const { return m_rows * m_cols; } unsigned count() const { return size(); } unsigned capacity() const { return size(); } public: Matrix_Dense& operator= (const Matrix_Dense &o) { return copy(o); } Matrix_Dense& copy( const Matrix_Dense &o ); Matrix_Dense& move( Matrix_Dense &o ); Matrix_Dense& swap( Matrix_Dense &o ); void clear(); public: bool equals( const Matrix_Dense & 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() { return E(0); } public: template friend bool check_ok( const Matrix_Dense& M ); template friend class test_Matrix; ///< BUnit test. private: // Rep typename Matrix_BASE::value_type * m_val; unsigned m_rows; unsigned m_cols; private: template friend void add_Matrix( Matrix_Dense& Res, const Matrix_Dense& M ); }; // Matrix_Dense #ifdef Spanish_dox /// \fn template Matrix_Dense::m_val; /// \brief Vector de valores de la matriz. /// \fn template Matrix_Dense::m_rows; /// \brief Cantidad de filas de la matriz. /// \fn template Matrix_Dense::m_cols; /// \brief Cantidad de columnas de la matriz. #endif #ifdef English_dox /// \fn template Matrix_Dense::m_val; /// \brief Vector to hold the matrix's values. /// \fn template Matrix_Dense::m_rows; /// \brief Number of rows of the matrix. /// \fn template Matrix_Dense::m_cols; /// \brief Number of columns of the matrix. #endif #if 0 {{ // Rep ==> Diagrama de la clase +---+ / \ | 2 | M(i,j) ==> m_val[ (i * m_cols) + j ] | 0 1 2 3 | m_rows == 2 +---+ (almacenamiento por filas) | 4 5 6 7 | m_cols == 4 | 4 | \ / +---+ +---+---+---+---+---+---+---+---+ | *-|-->| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | +---+ +---+---+---+---+---+---+---+---+ +---+ | 4 | M(i,j) ==> m_val[ i + (j * m_rows) ] / a e \ +---+ (almacenamiento por columnas) | b f | m_rows == 4 | 2 | | c g | m_cols == 2 +---+ +---+---+---+---+---+---+---+---+ \ d h / | *-|-->| a | b | c | d | e | f | g | h | +---+ +---+---+---+---+---+---+---+---+ }} #endif #ifdef Spanish_dox /** Verifica la invariante de la clase. \dontinclude Matrix_Dense.h \skipline Rep ==> Diagrama de la clase \until }} \copydetails check_ok_Matrix( const MAT& M ) */ #endif #ifdef English_dox /** Checks the class invariant. \dontinclude Matrix_Dense.h \skipline Rep ==> Diagrama de la clase \until }} \copydetails check_ok_Matrix(const MAT&) */ #endif template bool check_ok( const Matrix_Dense& M ) { if ( ! (&M != 0 ) ) { return false; /// - check_ok(): &M != 0 . } if ( M.m_rows == 0 || M.m_cols == 0 ) { // si alguno es cero... if ( M.m_rows != 0 || M.m_cols != 0 ) { // ... los 2 deben serlo return false; /// - check_ok(): (M.m_rows == 0) <==> (M.m_cols == 0) } else { if (M.m_val != 0) { return false; /// - check_ok(): (M.m_rows == 0) <==> (M.m_val == 0) } return true; // La implementación sí permite (m_val == 0) } } unsigned MxN = M.m_rows * M.m_cols; for (unsigned k=0; kcheck_ok( m_val[k] ) } } return true; } /// \copydoc Matrix_BASE::Matrix_BASE(unsigned,unsigned) template inline Matrix_Dense::Matrix_Dense(unsigned m, unsigned n) { if (m == 0 || n == 0) { m_rows = m_cols = 0; m_val = 0; } else { m_rows = m; m_cols = n; m_val = new typename Matrix_BASE::value_type [n*m]; } } /// \copydoc Matrix_BASE::Matrix_BASE(const Matrix_BASE&) template Matrix_Dense::Matrix_Dense(const Matrix_Dense& o) { if (o.m_val == 0) { // OJO: una matriz "vacía" produce errores en m_rows = m_cols = 0; // Matrix_Dense::operator(unsigned, unsigned) m_val = 0; return; } m_rows = o.m_rows; m_cols = o.m_cols; const unsigned MxN = o.m_rows * o.m_cols; m_val = new typename Matrix_BASE::value_type [MxN]; // como las matrices son del mismo tamaño, // basta copiarlas entrada por entrada. typename Matrix_BASE::value_type *pSelf = this->m_val; typename Matrix_BASE::value_type *pO = o.m_val; typename Matrix_BASE::value_type *pEND = &m_val[MxN]; for (; pSelf != pEND; ++pSelf, ++pO) { *pSelf = *pO; } } /// \copydoc Matrix_BASE::~Matrix_BASE() template inline Matrix_Dense::~Matrix_Dense() { if ( m_val != 0 ) { delete [] m_val; } } /// \copydoc Matrix_BASE::equals() // \copybrief Matrix_BASE::equals() \copydetails Matrix_BASE::equals() template bool Matrix_Dense::equals( const Matrix_Dense & o ) const { if (m_rows != o.m_rows || m_cols != o.m_cols) { return false; } else if ( m_val == 0 ) { return true; // las 2 matrices están vacías ==> son iguales } const unsigned MxN = o.m_rows * o.m_cols; typename Matrix_BASE::value_type *pSelf = this->m_val; typename Matrix_BASE::value_type *pO = o.m_val; typename Matrix_BASE::value_type *pEND = &m_val[MxN]; for (; pSelf != pEND; ++pSelf, ++pO) { if (*pSelf != *pO) { return false; } } return true; } /// \copydoc Matrix_BASE::copy() template Matrix_Dense& Matrix_Dense::copy( const Matrix_Dense &o ) { if (this == &o) { // evita auto-borrado return *this; } else if (o.m_val == 0) { if (m_val != 0) { delete [] m_val; m_rows = m_cols = 0; m_val = 0; } return *this; } // se asegura de que las dos matrices son del mismo tamaño const unsigned MxN = o.m_rows * o.m_cols; if (MxN != m_rows * m_cols) { // truco para evitar borrar la memoria dinámica delete [] m_val; m_val = new typename Matrix_BASE::value_type [MxN]; } m_rows = o.m_rows; m_cols = o.m_cols; // como las matrices son del mismo tamaño, // basta copiarlas entrada por entrada. typename Matrix_BASE::value_type *pSelf = this->m_val; typename Matrix_BASE::value_type *pO = o.m_val; typename Matrix_BASE::value_type *pEND = &m_val[MxN]; for (; pSelf != pEND; ++pSelf, ++pO) { *pSelf = *pO; } return *this; } /// \copydoc Matrix_BASE::move() template Matrix_Dense& Matrix_Dense::move( Matrix_Dense &o ) { if (this == &o) { // evita auto-borrado return *this; } else if (o.m_val == 0) { if (m_val != 0) { delete [] m_val; m_rows = m_cols = 0; m_val = 0; } return *this; } else if ( m_val != 0 ) { delete [] m_val; } m_val = o.m_val; // me robo los valores de "o" m_rows = o.m_rows; m_cols = o.m_cols; o.m_rows = o.m_cols = 0; o.m_val = 0; return *this; } /// \copydoc Matrix_BASE::swap() template inline Matrix_Dense& Matrix_Dense::swap( Matrix_Dense &o ) { std::swap( this->m_val , o.m_val ); std::swap( this->m_rows , o.m_rows ); std::swap( this->m_cols , o.m_cols ); return *this; } /// \copydoc Matrix_BASE::clear() template inline void Matrix_Dense::clear( ) { clear_Matrix( *this ); } /// \copydoc Matrix_BASE::reSize() template void Matrix_Dense::reSize(unsigned m, unsigned n) { const unsigned MxN = m * n; if (MxN == 0) { if (m_val != 0) { // sólo borra si hay algo que borrar delete [] m_val; m_rows = m_cols = 0; m_val = 0; } } else if ( MxN == m_rows * m_cols ) { // truco para evitar borrar la memoria dinámica m_rows = m; m_cols = n; } else { if (m_val != 0) { delete [] m_val; } m_val = new typename Matrix_BASE::value_type [MxN] ; m_rows = m; m_cols = n; } return; /* NOTA Esta es la antigua especificación de reSize(). Es incorrecta porque presume que el Rep de la matriz es un vector denso en donde están almacenados todos los valores de la matriz: +----------------------------------------------------------+ | reSize(): Le cambia las dimensiones a la matriz. | | ======== | | - Si ocurre que (m*n) == rows() * cols() los valores de | | la matriz se mantienen, aunque cambian sus dimensiones.| | - Si (m*n) != rows() * cols() los valores de la matriz | | quedarán inicializados de la misma forma en que los | | inicializaría CERO == Sparse_Matrix::value_type(). | | | | \pre (m > 0) && (n > 0) | +----------------------------------------------------------+ En la actual especificación, que ya está corregida, no queda implícita la presunción sobre cómo está organizada internamente la matriz. Por eso, esta nueva especificación sí sirve para una matriz implementada con un vector denso de valores, o para la matriz implementada como una matriz rala. Estos pequeños detalles en algunas ocasiones surgen cuando el programador de una clase introduce mejoras o modificaciones, pues muchas veces es muy difícil o prácticamente imposible predecir todos los pormenores y detalles de una especificación o de una implementación. */ } /// \copydoc Matrix_BASE::reShape() template void Matrix_Dense::reShape(unsigned m, unsigned n) { const unsigned MxN = m * n; if (MxN == m_rows * m_cols) { m_rows = m; m_cols = n; } } /// \copydoc Matrix_BASE::operator()(unsigned,unsigned) const template inline const E& Matrix_Dense::operator()(unsigned i, unsigned j) const { assert( "Matrix_Dense::operator()()" && (i < rows()) ); assert( "Matrix_Dense::operator()()" && (j < cols()) ); return m_val[ (i * m_cols) + j ] ; // almacena los valores por filas // return m_val[ i + (j * m_rows) ] ; // almacena los valores por columnas } /// \copydoc const_reference Matrix_BASE::operator()(unsigned,unsigned) template inline E& Matrix_Dense::operator()(unsigned i, unsigned j) { assert( "Matrix_Dense::operator()()" && (i < rows()) ); assert( "Matrix_Dense::operator()()" && (j < cols()) ); return m_val[ (i * m_cols) + j ] ; // almacena los valores por filas // return m_val[ i + (j * m_rows) ] ; // almacena los valores por columnas } /// \copybrief Matrix_BASE::add_Matrix() /// \copydetails Matrix_BASE::add_Matrix() template void add_Matrix( Matrix_Dense& Res, const Matrix_Dense & M ) { // verifica que las dos matrices sean del mismo tamaño assert( "Matrix_Dense::add()" && ( Res.rows() == M.rows() ) ); assert( "Matrix_Dense::add()" && ( Res.cols() == M.cols() ) ); // Como las matrices son del mismo tamaño basta sumarlas entrada por entrada. T *pRes = Res.m_val; T *pM = & M.m_val[0]; T *pEND = & Res.m_val[M.m_cols * M.m_rows]; for ( ; pRes != pEND; ++pRes, ++pM ) { *pRes += *pM; } return; } #if 0 // evita que esta implementación sea compilada /* NOTA Como esta rutina no está declarada, en la implementación de la resta se usa la función substract_Matrix() definida en Matrix_BASE.h, que es la versión genérica. Las funciones add_Matrix() y substract_Matrix() son ejemplo de que es posible redefinir selectivamente algunas de las implementaciones genéricas que aparecen en la clase base, que en este caso es Matrix_BASE<>. */ /// \copydoc Matrix_BASE::substract() template void substract_Matrix( Matrix_Dense& Res, const Matrix_Dense & M ) { // verifica que las dos matrices sean del mismo tamaño assert( "Matrix_Dense::substract()" && ( Res.rows() == M.rows() ) ); assert( "Matrix_Dense::substract()" && ( Res.cols() == M.cols() ) ); // Como las matrices son del mismo tamaño basta restarlas entrada por entrada. typename Matrix_BASE::value_type *pRes = Res.m_val; typename Matrix_BASE::value_type *pM = & M.m_val[0]; typename Matrix_BASE::value_type *pEND = & Res.m_val[M.m_cols * M.m_rows]; for ( ; pRes != pEND; ++pRes, ++pM ) { *pRes -= *pM; } return; } #endif } // namespace Mx #ifdef Spanish_dox /// \c "Doxygen: Documentación en español" #define Spanish_dox "Doxygen: Documentación en español" /// \def Matrix_Dense_h /// \brief Evita la inclusión múltiple. #endif #ifdef English_dox /// "Doxygen: English documentation" #define English_dox "Doxygen: English documentation" /// \def Matrix_Dense_h /// \brief Avoids multiple inclusion. #endif #endif // Matrix_Dense_h // EOF: Matrix_Dense.h