Página principal | Lista de namespace | Lista de componentes | Lista de archivos | Miembros del Namespace  | Miembros de las clases | Archivos de los miembros

Sparse_Matrix.h

Ir a la documentación de este archivo.
00001 // Sparse_Matrix.h Copyright (C) 2004  adolfo@di-mare.com
00002 
00003 /** \file  Sparse_Matrix.h
00004     \brief Declaraciones y definiciones para la clase \c Sparse_Matrix.
00005 
00006     \author Adolfo Di Mare <adolfo@di-mare.com>
00007     \date   2004
00008 
00009     - Why English names??? ==> http://www.di-mare.com/adolfo/binder/c01.htm#sc04
00010 */
00011 
00012 #ifndef   Sparse_Matrix_h
00013 #define   Sparse_Matrix_h
00014 
00015 #include <cassert> // assert()
00016 
00017 /// Definido por la biblioteca C++ estándar
00018 namespace std {} // Está acá para que Doxygen lo documente
00019 
00020 /// Matriz chirrisquitica de adolfo@di-mare.com
00021 namespace Mx {
00022 
00023 /** Esta es una clase matriz muy chirrisquitica almacenada como una matriz rala.
00024     - La matriz tiene tamaño \c rows() x \c cols().
00025     - Se le puede cambiar el tamaño dinámicamente con el método \c reSize().
00026     - La clase \c E debe incluir un neutro para la adición, cuyo valor debe poderse
00027       obtener invocando el convertidor \c Sparse_Matrix<E>::value_type(0).
00028     - Las operaciones aritméticas "+" "-" "*" deben estar definidas para
00029       \c Sparse_Matrix<E>::value_type y debe existir el valor \c Sparse_Matrix<E>::value_type(0).
00030     \see http://www.oonumerics.org/oon/
00031 */
00032 template <class E>
00033 class Sparse_Matrix {
00034 public:
00035     /// Tipo del objeto almacenado, similar al nombre usado en STL
00036     typedef E value_type;
00037     /// Tipo del objeto almacenado, similar al nombre usado en STL
00038     typedef value_type& reference;
00039     /// Tipo del objeto almacenado, similar al nombre usado en STL
00040     typedef const value_type& const_reference;
00041     /// Tipo del tamaño de un objeto, similar al nombre usado en STL
00042     typedef unsigned size_type;
00043 public:
00044     Sparse_Matrix(unsigned m = 1, unsigned n = 1);
00045     Sparse_Matrix(const Sparse_Matrix& o); ///< Constructor de copia
00046     /// Matriz escalar de valor \c V.
00047     Sparse_Matrix(const value_type V) : m_capacity(0) { reSize(1,1); (*this)(0,0) = V; }
00048     ~Sparse_Matrix();
00049 public:
00050     unsigned count() const { return size(); } ///< Cantidad de valores almacenados en la matriz
00051     unsigned size()  const { return m_rows * m_cols; } ///< Cantidad de valores almacenados en la matriz
00052     unsigned rows() const { return m_rows; } ///< Cantidad de filas de la matriz
00053     unsigned cols() const { return m_cols; } ///< Cantidad de columnas de la Matriz
00054     /// Cantidad máxima posible de valores diferentes que pueden ser almacenados en la matriz
00055     size_type capacity() const { return m_capacity; }
00056 public:
00057     Sparse_Matrix& operator= (const Sparse_Matrix &o) { return copy(o); } ///< Sinónimo de \c this->copy(o)
00058     Sparse_Matrix& copy( const Sparse_Matrix &o );
00059     Sparse_Matrix& move( Sparse_Matrix &o );
00060     Sparse_Matrix& swap( Sparse_Matrix &o );
00061 public:
00062     /// ¿¿¿ (p == q) ???
00063     friend bool operator == (const Sparse_Matrix &p, const Sparse_Matrix &q) { return p.equals(q); }
00064     /// ¿¿¿ (p != q) ???
00065     friend bool operator != (const Sparse_Matrix &p, const Sparse_Matrix &q) { return !(p==q); }
00066     bool equals( const Sparse_Matrix & o ) const; ///< ¿¿¿ (*this==o) ???
00067     /// Retorna \c true si \c "o" comparte sus valores con \c "*this"
00068     bool same( const Sparse_Matrix & o ) const { return this == &o; }
00069 private:
00070     void add( const Sparse_Matrix & );
00071     void substract( const Sparse_Matrix & );
00072     void multiply( const Sparse_Matrix &, const Sparse_Matrix & );
00073 public:
00074     friend Sparse_Matrix operator + (const Sparse_Matrix& A, const Sparse_Matrix& B)
00075     { Sparse_Matrix Res = A; Res.add(B); return Res; } ///< Retorna \c A+B
00076     friend Sparse_Matrix operator - (const Sparse_Matrix& A, const Sparse_Matrix& B)
00077     { Sparse_Matrix Res = A; Res.substract(B); return Res; } ///< Retorna \c A-B
00078     friend Sparse_Matrix operator * (const Sparse_Matrix& A, const Sparse_Matrix& B)
00079     { Sparse_Matrix Res;  Res.multiply(A, B); return Res; } ///< Retorna \c A*B
00080 public:
00081     reference operator () (unsigned, unsigned);
00082     const_reference operator () (unsigned, unsigned) const;
00083 public:
00084     void reSize( unsigned, unsigned);
00085     void reShape(unsigned, unsigned);
00086 public:
00087     void setDefault(const E& same);
00088     /// Valor almacenado en la mayor parte de la \c Sparse_Matrix
00089     const E& getDefault() { return m_same; }
00090     /// Ajusta la matriz para que pueda almacenar \c n valores diferentes a \c getDefault().
00091     void reserve(size_type _Count);
00092     void reSize(unsigned newsize);
00093     void clear(); ///< Borra todos los valores de la \c Sparse_Matrix
00094 
00095     template<class T> friend bool  check_ok( const Sparse_Matrix<T>& M );
00096     template<class T> friend class test_Sparse_Matrix; ///< Datos de prueba para la clase
00097 private:
00098     unsigned* m_I;   ///< Indice "i" de \c M(i,j) <code>0 <= i < m_capacity</code>
00099     unsigned* m_J;   ///< Indice "j" de \c M(i,j) <code>0 <= i < m_capacity</code>
00100     E       * m_val; ///< Valor para \c M(i,j) <code>0 <= i < m_capacity</code>
00101     unsigned  m_size;   ///< Cantidad de valores insertados en los 3 vectores paralelos
00102     unsigned  m_capacity; ///< Tamaño de los 3 vectores paralelos
00103     unsigned  m_rows; ///< Cantidad de filas de la matriz
00104     unsigned  m_cols; ///< Cantidad de columnas de la matris
00105     E         m_same; ///< Valor almacenado en la mayor parte de la \c Sparse_Matrix
00106 }; // Sparse_Matrix
00107 
00108 /** Verifica la invariante de la clase.
00109     - El campo \c m_same indica cuál es el valor que se repite más en toda la matriz.
00110     - Usualmente \c same es el neutro aditivo \c value_type(0).
00111     - No existe un constructor explícito para darle a \c m_same su valor inicial, que
00112       es siempre inicializado en \c value_type(0). Para cambiarlo es necesario invocar
00113       el método \c setgetDefault().
00114     - Los vectores \c m_I[], \c m_J[] y \c m_val[] son vectores paralelos, todos de
00115       longitud \c Sparse_Matrix::m_capacity.
00116     - La cantidad máxima de valores diferente que pueden ser almacenados en la matriz
00117       es \c Sparse_Matrix::m_capacity.
00118     - El largo de estos vectores aumenta cuando se hace referencia a un valor M(i,j)
00119       mediante la versión que no es \c const del \c operator()(i,j). O sea, que es
00120       ese método el encargado de agregar valores en \c m_val[], pues el operador
00121       homónimo \c const operator()(i,j) nunca agrega nada y, como es \c const, en
00122       general retorna una referencia constante a \c m_same.
00123     - Es posible que la matriz tenga dimensiones nulas, lo que implica que todos los
00124       punteros a los vectors paralelos deben ser nulos. Este hecho se marca dándolo
00125       el valor \c 0 (cero) al campo \c m_capacity.
00126     - En todos los algoritmos, "m" o "m_rows" es la cantidad de filas == \c rows()
00127     - En todos los algoritmos, "n" o "m_cols" es la cantidad de columnas == \c cols()
00128 
00129     \par <em>Rep</em> Modelo de la clase
00130 \code
00131          ____________________________________
00132         /          m_capacity                \
00133         +---+---+---+---+---+---+-..-+---+---+       0 1 2 3 4 5 6
00134  m_I--->| 1 | 3 | 3 |'-'| ...       ...  |'-'|   0 / - - - - - - - \
00135         +---+---+---+---+ ...       ...  +---+   1 | - a - - - - - |
00136  m_J--->| 1 | 2 | 1 |'-'| ...       ...  |'-'|   2 | - - - - - - - |
00137         +---+---+---+---+ ...       ...  +---+   3 | - c b - - - - |
00138 m_val-->|'a'|'b'|'c'|'-'| ...       ...  |'-'|   4 \ - - - - - - - /
00139         +---+---+---+---+---+---+-..-+---+---+
00140           0   1   2   |
00141         m_size--------+ == 3    m_same == '-'   m_rows == 5  m_cols == 7
00142 \endcode
00143     - http://www.di-mare.com/adolfo/binder/c03.htm#k1-Rep
00144     \remark
00145     Libera al programador de implementar el método \c Ok()
00146     - http://www.di-mare.com/adolfo/binder/c04.htm#sc11
00147 */
00148 template<class T>
00149 bool check_ok( const Sparse_Matrix<T>& M ) {
00150     if ( M.m_capacity == 0 ) { // m_capacity es el "marcador" que determina el valor de los demás
00151         if ( M.m_I != 0 ) {
00152             return false; /// - Invariante: <code>(m_capacity == 0) <==> (m_I == 0)</code>
00153         }
00154         if ( M.m_J != 0 ) {
00155             return false; /// - Invariante: <code>(m_capacity == 0) <==> (m_J == 0)</code>
00156         }
00157         if ( M.m_val != 0 ) {
00158             return false; /// - Invariante: <code>(m_capacity == 0) <==> (m_val == 0)</code>
00159         }
00160     }
00161     else {
00162         if ( M.m_rows == 0 ) {
00163             return false; /// - Invariante: <code>(m_rows == 0) ==> (m_capacity == 0)</code>
00164         }
00165         if ( M.m_cols == 0 ) {
00166             return false; /// - Invariante: <code>(m_cols == 0) ==> (m_capacity == 0)</code>
00167         }
00168         if ( M.m_I == 0 ) {
00169             return false; /// - Invariante: <code>(m_capacity != 0) <==> (m_I != 0)</code>
00170         }
00171         if ( M.m_J == 0 ) {
00172             return false; /// - Invariante: <code>(m_capacity != 0) <==> (m_J != 0)</code>
00173         }
00174         if ( M.m_val == 0 ) {
00175             return false; /// - Invariante: <code>(m_capacity != 0) <==> (m_val != 0)</code>
00176         }
00177     }
00178 
00179     if (M.m_rows == 0 || M.m_cols == 0) {
00180         if (M.m_rows == 0 && M.m_cols == 0) {
00181             // OK ==> los 2 son nulos
00182         }
00183         else {
00184             return false;  /// - Invariante: <code>(m_rows == 0) <==> (m_cols == 0)</code>
00185         }
00186     }
00187 
00188     if ( M.m_size > M.m_capacity ) {
00189         return false; /// - Invariante: <code>( m_size <= m_capacity )</code>
00190     }
00191     if ( ! check_ok (M.m_same) ) {
00192         return false; /// - Invariante: <code>check_ok (m_same)</code>
00193     }
00194     for (unsigned k=0; k<M.m_size; ++k) {
00195         if ( ! ( (0<=M.m_I[k]) && (M.m_I[k]<M.m_rows) ) ) {
00196             return false; /// - Invariante: <code>( (0<=m_I[k]) && (m_I[k] < m_rows) ) k = [0..m_size-1]</code>
00197         }
00198         if ( ! ( (0<=M.m_J[k]) && (M.m_J[k]<M.m_cols) ) ) {
00199             return false; /// - Invariante: <code>( (0<=m_J[k]) && (m_J[k] < m_cols) ) k = [0..m_size-1]</code>
00200         }
00201         if ( ! check_ok( M.m_val[k] ) ) {
00202             return false; /// - Invariante: <code>check_ok( m_val[k] )</code>
00203         }
00204     }
00205     return true;
00206 }
00207 
00208 /// Define el escalar que por defecto está en todas las entradas de la \c Sparse_Matrix.
00209 /// - Si <code>same != getDefault()</code> la matriz queda vacía.
00210 /// - De lo contrario, nada ocurre.
00211 template<class E>
00212 inline void Sparse_Matrix<E>::setDefault(const E& same) {
00213     if ( m_same != same ) {
00214         m_same = same;
00215         m_size = 0;
00216     }
00217 }
00218 /** \fn template <class E>inline Sparse_Matrix<E>::Sparse_Matrix(const value_type V);
00219 
00220     \brief Constructor a partir de \c Sparse_Matrix<E>::value_type(V).
00221 
00222     - La matriz resultante es una matriz escalar, de dimensiones 1x1,
00223       y su valor es \c "V"
00224 */
00225 
00226 /** Constructor de vector.
00227     - Obtiene suficiente memoria dinámica para almacenas los
00228       <code> n * m </code> valores de la matriz.
00229     - Si \c "value_type" tiene un constructor de vector, lo
00230       usar para inicializar cada uno de los valores de la matriz;
00231       de lo contrario, los deja tal cual están en la memoria.
00232     - Si \c "value_type" es uno de los tipos escalares básicos,
00233       como lo son \c int o \c float, los valores almacenados
00234       en la matriz quedan tal cual están y no son inicializados.
00235     \pre
00236      -  <code> m * n > 0  </code>
00237      -  <code> (m > 0) && (n > 0) </code>
00238 */
00239 template <class E>
00240 inline Sparse_Matrix<E>::Sparse_Matrix(unsigned m, unsigned n) {
00241     if (m == 0 || n == 0) {
00242         m_rows = m_cols = 0;
00243         m_val = 0;
00244         m_capacity = 0;
00245     } else {
00246         m_rows = m;
00247         m_cols = n;
00248 
00249         m_capacity = 16; // pues 16 == 2*2*2*2 ...
00250         m_I   = new size_type [m_capacity];
00251         m_J   = new size_type [m_capacity];
00252         m_val = new value_type[m_capacity];
00253     }
00254 
00255     m_size = 0;
00256     m_same = E(0); // Convierte el número "cero" en el neutro tipo "E"
00257 }
00258 
00259 template <class E>
00260 inline Sparse_Matrix<E>::Sparse_Matrix(const Sparse_Matrix<E>& o) {
00261     m_capacity = 0; // m_capacity==0 indica que NO se está usando memoria dinámica
00262     copy( o );     // assert( this != &o );
00263     return;       // ... pues ya "o" existe y se está usando para incializar a "*this"
00264 /*  NOTA
00265     Cuando un objeto usa memoria dinámica, copiarlo es un proceso diferente a
00266     inicializarlo a partir de otro valor de su misma clase. En otras palabras,
00267     el trabajo que hace el constructor CLS::CLS( const CLS& ) es muy diferente
00268     al que hace el copiador CLS& operator=( const CLS & ).
00269 
00270     El truco de programación usado en en esta implementación consiste en "marcar"
00271     el valor "m_capacity" para saber si se ha obtenido o no memoria dinámica
00272     para los vectores paralelos. La implementación de copy() está hecah de manera
00273     que si "m_capacity" es un puntero nulo, en copy() se inicializan correctamente
00274     todos los del Rep de Sparse_Matrix. Eso explica por qué la implementación de
00275     copy() es un tanto más elaborada que lo que a primera vista pareciera que es
00276     necesario.
00277 */
00278 }
00279 
00280 /// Destructor
00281 template <class E>
00282 inline Sparse_Matrix<E>::~Sparse_Matrix() {
00283     if (m_capacity != 0) { // sólo devuelve la memoria dinámica que sí ha adquirido
00284         delete [] m_I;
00285         delete [] m_J; // Retorna la memoria dinámica en uso
00286         delete [] m_val;
00287     }
00288 }
00289 
00290 template <class E>
00291 bool Sparse_Matrix<E>::equals( const Sparse_Matrix & o ) const {
00292     if ( this == & o ) {
00293         return true;
00294     }
00295     if (rows() != o.rows() || cols() != o.cols()) {
00296         return false;
00297     }
00298 
00299     for (unsigned i=0; i<rows(); i++) {
00300         for (unsigned j=0; j<cols(); j++) {
00301             if ( (*this)(i,j) != o(i,j) ) {
00302                 return false;
00303             }
00304         }
00305     }
00306     return true;
00307 }
00308 
00309 /** Copia desde \c "o".
00310     - Copia todo el valor de \c "o" sobre \c "*this", de forma que el nuevo valor de
00311       \c "*this" sea un duplicado exacto del valor de \c "o".
00312     - El valor anterior de \c "*this" se pierde.
00313     - El objeto \c "o" mantiene su valor anterior.
00314     - Luego de la copia, cuando el valor de \c "o" cambia, el de \c "*this" no cambiará,
00315       y viceversa, pues la copia es una copia profunda; no es superficial.
00316     - Si \c "*this" es \c "o" entonces su valor no cambia.
00317     - Después de esta operación siempre ocurre que <code> *this == o </code>.
00318 
00319     \par Complejidad:
00320          O( <code>  rows() * cols() </code> )
00321 
00322     \returns *this
00323     \see http://www.di-mare.com/adolfo/binder/c04.htm#sc05
00324 */
00325 template <class E>
00326 Sparse_Matrix<E>& Sparse_Matrix<E>::copy( const Sparse_Matrix<E> &o ) {
00327     if (this == &o) { // evita auto-borrado
00328         return *this;
00329     }
00330     if (o.m_capacity == 0) {
00331         if (m_capacity != 0) { // Como copy() puede ser invocado cuando el Rep
00332             delete [] m_val;   // NO ha sido inicializado, en esta implementación
00333             delete [] m_J;     // hay que tener cuidado de no usar los otros campos
00334             delete [] m_I;     // del Rep antes de darles valor.
00335         }                      // - "m_capacity" SI debe tener su valor correcto.
00336         m_rows = m_cols = 0;
00337         m_val = 0;
00338         m_same = o.m_same;
00339         m_size = 0;       //  assert( o.m_size == 0 );
00340         m_capacity = 0;   //  assert( o.m_capacity == 0 );
00341         return *this;
00342     }
00343 
00344     // se asegura de que las dos matrices sean del mismo tamaño
00345     if (m_capacity != o.m_capacity) { // truco para evitar borrar la memoria dinámica
00346         if (m_capacity != 0) { // sólo devuelve la memoria dinámica que sí ha adquirido
00347             delete [] m_val;
00348             delete [] m_J;
00349             delete [] m_I;
00350         }
00351 
00352         m_capacity = o.m_capacity;
00353         m_I   = new size_type [m_capacity];
00354         m_J   = new size_type [m_capacity];
00355         m_val = new value_type[m_capacity];
00356     }
00357     m_same = o.m_same;
00358     m_size = o.m_size;
00359 
00360     // como las matrices son del mismo tamaño,
00361     // basta copiarlas entrada por entrada.
00362     m_rows = o.m_rows;
00363     m_cols = o.m_cols;
00364     for (unsigned k=0; k<m_size; ++k) {
00365         m_I[k]   = o.m_I[k];
00366         m_J[k]   = o.m_J[k];
00367         m_val[k] = o.m_val[k];
00368     }
00369     return *this;
00370 }
00371 
00372 /** Traslada el valor de \c "o" a \c "*this".
00373   - El valor anterior de \c "*this" se pierde.
00374   - El nuevo valor de \c "*this" es el que \c "o" tuvo.
00375   - \c "o" queda en el estado en que lo dejaría \c Erase().
00376   - Si \c "*this" es \c "o" entonces su valor no cambia.
00377   - En general, después de esta operación casi
00378     nunca ocurre que <code> (*this == o) </code>
00379 
00380     \par Complejidad:
00381          O( <code>  rows() * cols() </code> )
00382 
00383     \returns \c *this
00384 
00385     \see http://www.di-mare.com/adolfo/binder/c04.htm#sc07
00386 */
00387 template <class E>
00388 Sparse_Matrix<E>& Sparse_Matrix<E>::move( Sparse_Matrix<E> &o ) {
00389     if (this == &o) { // evita auto-borrado
00390         return *this;
00391     } else if (o.m_val == 0) {
00392         if (m_val != 0) {
00393             delete [] m_val;
00394         }
00395         m_rows = m_cols = 0;
00396         m_val = 0;
00397         return *this;
00398     } else if ( m_val != 0 ) {
00399         delete [] m_val;
00400     }
00401     m_val = o.m_val;
00402     m_rows = o.m_rows;
00403     m_cols = o.m_cols;
00404 
00405     o.m_val = 0;
00406     o.m_rows = o.m_cols = 0;
00407     return *this;
00408 }
00409 
00410 /** Intercambia los valores de \c "*this" y \c "o".
00411     - Debido a que algunos métodos retornan copias del valor de \c "*this" en
00412       lugar de una referencia, como ocurre con \c Sparse_Matrix::Child(), a veces
00413       \c swap() no tiene el resultado esperado por el programador.
00414     - Por ejemplo, si se invoca <code> T.Child(i). swap( T.Child(j) ) </code>
00415       el resultado no es intercambiar los hijos, sino más bien intercambiar
00416       los valores de los sub-árboles temporales \c T.Child(i) y \c T.Child(j).
00417       La forma correcta de intercambiar hijos es usar \c Graft().
00418 
00419       \par Complejidad:
00420          O( \c 1 )
00421 
00422     \returns *this
00423 
00424     \see http://www.di-mare.com/adolfo/binder/c04.htm#sc08
00425 */
00426 template <class E>
00427 inline Sparse_Matrix<E>& Sparse_Matrix<E>::swap( Sparse_Matrix<E> &o ) {
00428     std::swap( this->m_I        , o.m_I        );
00429     std::swap( this->m_J        , o.m_J        );
00430     std::swap( this->m_val      , o.m_val      );
00431     std::swap( this->m_size     , o.m_size     );
00432     std::swap( this->m_capacity , o.m_capacity );
00433     std::swap( this->m_rows     , o.m_rows     );
00434     std::swap( this->m_cols     , o.m_cols     );
00435     std::swap( this->m_same     , o.m_same     );
00436     return *this;
00437 }
00438 
00439 /** Le cambia las dimensiones a la matriz.
00440     - En algunos casos los valores almacenados en la matriz no quedan
00441       todos iguales a \c Sparse_Matrix<E>::value_type(0).
00442     \pre <code> (m > 0) && (n > 0) </code>
00443 */
00444 template <class E>
00445 void Sparse_Matrix<E>::reSize(unsigned m, unsigned n) {
00446     // NO hace falta cambiar m_capacity porque lo único que está cambiando
00447     // el la dimensión de la matriz
00448     if (m != m_rows || n != m_cols) {
00449         m_size = 0; // desecha todos los valores anteriores
00450         m_rows = m; // cambia las dimensiones de la matriz
00451         m_cols = n;
00452     }
00453 
00454     if (m==0 || n==0) {
00455         m_rows = 0;
00456         m_cols = 0;
00457         m_size = 0;
00458         if (m_capacity != 0) {
00459             m_capacity = 0; // todo esto mantiene la invariante de la clase
00460             delete [] m_I;     m_I = 0;
00461             delete [] m_J;     m_J = 0;
00462             delete [] m_val;   m_val = 0;
00463         }
00464     }
00465     return;
00466 
00467 #if 0
00468 /*  NOTA
00469     Esta es la antigua especificación de reSize(). Es incorrecta
00470     porque presume que el Rep de la matriz es un vector denso en
00471     donde están almacenados todos los valores de la matriz:
00472 
00473     +----------------------------------------------------------+
00474     | reSize(): Le cambia las dimensiones a la matriz.         |
00475     | ========                                                 |
00476     | - Si ocurre que (m*n) == rows() * cols() los valores de  |
00477     |   la matriz se mantienen, aunque cambian sus dimensiones.|
00478     | - Si (m*n) != rows() * cols() los valores de la matriz   |
00479     |   quedarán inicializados de la misma forma en que los    |
00480     |   inicializaría CERO == Sparse_Matrix<E>::value_type(0). |
00481     |                                                          |
00482     | \pre (m > 0) && (n > 0)                                  |
00483     +----------------------------------------------------------+
00484 
00485     En la actual especificación, que ya está corregida, no queda
00486     implícita la presunción sobre cómo está organizada internamente
00487     la matriz. Por eso, esta nueva especificación sí sirve para una
00488     matriz implementada con un vector denso de valores, o para la
00489     matriz implementada como una matriz rala.
00490 
00491     Estos pequeños detalles en algunas ocasiones surgen cuando el
00492     programador de una clase introduce mejoras o modificaciones, pues
00493     muchas veces es muy difícil o prácticamente imposible predecir
00494     todos los pormenores y detalles de una especificación o de una
00495     implementación.
00496 */
00497 
00498 /*  NOTA
00499     Esta es la imnplementación anterior, que debe reacomodar los ínices
00500     (i,j) de aquellos valores almacenados en los vectores paralalos para
00501     simular que la matriz rala en realidad está almacenada como una
00502     matriz densa, dentro de un vector.
00503     - Al modificar la especificación de para reSize() ya no hace falta
00504     hacer todo este trabajo.
00505 
00506     NO hace falta cambiar m_capacity porque lo único que está cambiando
00507     el la dimensión de la matriz
00508 */
00509 
00510 /*  Nota para esta implementación:
00511     Este método funciona de una forma similar a reSize() para la matriz implementada con
00512     un vector. Para lograr que las clases Matrix<E> y Sparse_Matrix<E> tengan la misma
00513     funcionalidad, esta implementación reacomoda los índices de la matriz de manera que
00514     las m_rows*m_cols entradas queden reacomodadas como si la implementación usara un
00515     vector de dimension m*n. Sin embargo, lo lógico sería eliminar todos los valores
00516     de la matriz trocando el ciclo "for(;k;){}" por un eficiente "m_size=0;"
00517     - Esto muestra que una especificación que aparenta ser "amplia", como lo es la de
00518       Matrix<E>::reSize(n,m), en realidad se queda "corta" si se toma en cuenta que la
00519       misma especificación debe servir para varias implementaciones diferentes.
00520     - Además, muestra que algunas operaciones sólo tienen sentido para una implementación
00521       específica, como ocurre con las operaciones Sparse_Matrix<E>>::getDefault() y su
00522       "seteador" Sparse_Matrix<E>>::setgetDefault().
00523 */
00524     if (m * n == m_rows * m_cols) {
00525         m_size = 0; // desecha todos los valores anteriores
00526     }
00527     else {
00528         unsigned row = 0, col = 0;
00529         for (unsigned k=0; k<m_size; ++k) {
00530             // M(i,j) <==> m_val[ (i * m_cols) + j ] ; // si almacena los valores por filas
00531             // M(i,j) <==> m_val[ i + (j * m_rows) ] ; // si almacena los valores por columnas
00532 
00533             unsigned lineal = (m_I[k] * m_cols) + m_J[k]; // por filas
00534         //  unsigned lineal = m_I[k] + (m_j[k] * m_rows); // por columnas
00535             m_I[k] = lineal / n; // lineal % m;
00536             m_J[k] = lineal % n; // lineal / m;
00537         }
00538     }
00539     m_rows = m; // cambia las dimensiones de la matriz
00540     m_cols = n;
00541 
00542 #endif
00543 }
00544 
00545 /** Le ajusta las dimensiones a la matriz.
00546     - Si ocurre que <code> (m*n) == rows()*cols() </code> hace
00547       lo mismo que haría \c reSize(m,n).
00548     - En caso contrario, no hace nada.
00549 */
00550 template <class E>
00551 inline void Sparse_Matrix<E>::reShape(unsigned m, unsigned n) {
00552     if ( m * n == m_rows * m_cols ) {
00553         reSize(n,m);
00554     }
00555 }
00556 
00557 /// Retorna una referencia al elemento [i,j] de la matriz ( \c const ).
00558 /// - \c M(i,j) significa lo que en arreglos se denota con \c M[i][j].
00559 /// - <code>val = M(i,j); // M(i,j) es un "rvalue" (const)</code>
00560 template <class E>
00561 inline const E& Sparse_Matrix<E>::operator () (unsigned i, unsigned j) const {
00562     assert( "Sparse_Matrix<E>::operator()()" && (i < rows()) );
00563     assert( "Sparse_Matrix<E>::operator()()" && (j < cols()) );
00564 
00565     for (unsigned k=0; k<m_size; k++) {
00566         if ( m_I[k] == i ) {
00567             if ( m_J[k] == j ) {
00568                 return m_val[k];
00569             }
00570         }
00571     }
00572     return m_same;
00573 /*  NOTA
00574     Como este método es "const", de antemano se sabe que el programador no puede
00575     usarlo para modificar el valor de la matriz. Por eso, aunque el valor
00576     retornado sea una referencia a valor común por defecto m_same, de antemano
00577     el compilador asegura que ese valor no será modificado.
00578 
00579     Sin embargo, cuando el programador usa el método homónimo operator()(i,j)
00580     que no es "const", es posible que el valor retornado sí sea modificado.
00581     En ese caso, ya no es correcto retornar una referencia al valor común "m_same".
00582     - Por eso, cuando se usa el hace referencia en el otro operator()(i,j) es
00583       necesario agregar una entrada en los vectores paralelos en aquellos casos
00584       en que no existe un valor diferente a "m_same" para (i,j).
00585     - Esto quiere decir que sí es posible que al utilizar la versión modificable
00586       de operator()(i,j) quede en el vector "m_val[]" un valor que es igual a
00587       "m_same". En el caso peor, podría ocurrir que todos los valores almacenados
00588       en el vector "m_val[]" sean todos iguales a "m_same".
00589     - Una forma de corregir esta anomalía es "revisar después" si existe un valor
00590       en el vector "m_val[]" que es igual a "m_same". Una forma eficiente de
00591       hacerlo es mantener el el Rep un puntero "m_last_change" que apunte al
00592       último valor "m_val[]" que la versión modificable de operator()(i,j) retornó.
00593       En la siguiente invocación a operator()(i,j), se puede verificar si hubo un
00594       ese valor anterior fue cambiado de manera que tiene almacenado "m_same".
00595 */
00596 #if 0
00597     // Este código podría ser agregado al principio de operator()(i,j)
00598     if (m_last_change != 0) {
00599         if ( *m_last_change == m_same ) { // el último uso de op()(i,j) dejó "m_same"
00600             if ( m_size == 1 ) { // Elimina los vectores viejos
00601                 delete [] m_I;    m_I = 0;
00602                 delete [] m_J;    m_J = 0;
00603                 delete [] m_val;  m_val = 0;
00604                 m_capacity = 0;
00605                 m_size     = 0;
00606             }
00607             else { // intercambia este valor con el último
00608                 m_size--;
00609                 unsigned k = (m_last_change - m_val); // índice de "* m_last_change"
00610                 *m_last_change = m_val[m_size];
00611                 m_I[k] = m_I[m_size];
00612                 m_J[k] = m_J[m_size];
00613             }
00614         }
00615         m_last_change = 0;
00616     }
00617 #endif
00618 }
00619 /// Retorna una referencia al elemento [i,j] de la matriz.
00620 /// - \c M(i,j) significa lo que en arreglos se denota con \c M[i][j].
00621 /// - <code>M(i,j) = val; // M(i,j) es un "lvalue" (modificable)</code>
00622 template <class E>
00623 inline E& Sparse_Matrix<E>::operator() (unsigned i, unsigned j) {
00624     assert( "Sparse_Matrix<E>::operator()()" && (i < rows()) );
00625     assert( "Sparse_Matrix<E>::operator()()" && (j < cols()) );
00626 
00627     // Busca al elemento (i,j) en los vectores paralelos
00628     for (unsigned k=0; k<m_size; k++) {
00629         if ( m_I[k] == i ) {
00630             if ( m_J[k] == j ) {
00631             //  m_last_change = & m_val[k];
00632                 return m_val[k];
00633             }
00634         }
00635     }
00636 
00637     assert( (m_size <= m_capacity) && " => Agotado m_val[] Sparse_Matrix<E>::operator()()" );
00638     if (m_size == m_capacity) {
00639         unsigned new_capacity = m_capacity;
00640         if (m_capacity % 16 == 0) {
00641             new_capacity += 16;
00642         }
00643         else {
00644             new_capacity /= 16; // Hace que el nuevo tamaño sea divisible por 16
00645             new_capacity *= 16;
00646             new_capacity += 16;
00647         }
00648         reSize( new_capacity ); // Agrega 16 porque 16 == 2*2*2*2 ...
00649     }
00650     // Agrega un nuevo valor modificable en los vectores paralelos
00651     m_I[m_size] = i;
00652     m_J[m_size] = j;
00653     m_val[m_size] = m_same;
00654 //  m_last_change = & m_val[m_size];
00655     return m_val[m_size++];
00656 }
00657 
00658 /// Le cambia el tamaño máximo posible a la matriz.
00659 /// - Le aumenta la cantidad de valores diferentes a \c getDefault().
00660 /// - No hace nada cuando <code>size() < newsize</code>.
00661 template <class E>
00662 void Sparse_Matrix<E>::reSize(unsigned newsize) {
00663     if ( newsize < m_size ) { // hay más valores en uso que "newsize"
00664         // assert((m_size < newsize) && "=>Sparse_Matrix<E>::reSize()" );
00665         return;
00666     }
00667     unsigned * new_I   = new unsigned [ newsize ]; // Obtiene los vectores nuevos
00668     unsigned * new_J   = new unsigned [ newsize ];
00669     E        * new_val = new E        [ newsize ];
00670     if (m_capacity > 0) { // Copia los valores en uso
00671         for (unsigned k=0; k<m_size; ++k) {
00672             new_I[k]   = m_I[k];
00673             new_J[k]   = m_J[k];
00674             new_val[k] = m_val[k];
00675         }
00676         delete [] m_I;  // Elimina los vectores viejos
00677         delete [] m_J;
00678         delete [] m_val;
00679     }
00680     m_I = new_I;      // Instala los vectores nuevos
00681     m_J = new_J;
00682     m_val = new_val;
00683     m_capacity = newsize;
00684 }
00685 
00686 /** Le suma a \c "*this" la matriz \c "O".
00687     \pre
00688     - \c "*this" y \c "O" deben tener las mismas dimensiones
00689     - <code> rows() == O.rows() && cols() == O.cols() </code>
00690 
00691     \par Complejidad:
00692          O( <code> rows() * cols() </code> )
00693 
00694     \remarks
00695     - Esta es la implementación de Sparse_Matrix<E> operator+( Sparse_Matrix<E>&, Sparse_Matrix<E> )
00696     - El compilador tiene problemas en compilar un función amiga (<em>"friend"</em>)
00697       definida con plantillas si esa función amiga no está definida (implementada)
00698       dentro de la declaración de la clase. Para solventar esta deficiencia existe
00699       este método que realiza el trabajo, aunque es poco cómodo de usar.
00700 */
00701 template <class E>
00702 void Sparse_Matrix<E>::add( const Sparse_Matrix<E> & O ) {
00703     // verifica que las dos matrices sean del mismo tamaño
00704     assert( "Sparse_Matrix<E>::add()" && (rows() == O.rows()) && (cols() == O.cols()) );
00705 
00706     // crea una nueva matriz que va a ser la que se devuelve
00707     reSize(O.m_rows, O.m_cols);
00708 
00709     // Como las matrices son del mismo tamaño basta sumarlas entrada por entrada.
00710 
00711     value_type *pThis = m_val;
00712     value_type *pO    = & O.m_val[0];
00713     value_type *pEND  = &m_val[m_cols * m_rows];
00714     for ( ; pThis != pEND; ++pThis, ++pO) {
00715         *pThis += *pO;
00716     }
00717     return;
00718 }
00719 
00720 /** Le resta a \c "*this" la matriz \c "O".
00721     \pre
00722     - \c "*this" y \c "O" deben tener las mismas dimensiones
00723     - <code> rows() == O.rows() && cols() == O.cols() </code>
00724 
00725     \par Complejidad:
00726          O( <code> rows() * cols() </code> )
00727 
00728     \remarks
00729     - Esta es la implementación de Sparse_Matrix<E> operator-( Sparse_Matrix<E>&, Sparse_Matrix<E> )
00730     - El compilador tiene problemas en compilar un función amiga (<em>"friend"</em>)
00731       definida con plantillas si esa función amiga no está definida (implementada)
00732       dentro de la declaración de la clase. Para solventar esta deficiencia existe
00733       este método que realiza el trabajo, aunque es poco cómodo de usar.
00734 */
00735 template <class E>
00736 void Sparse_Matrix<E>::substract( const Sparse_Matrix<E> & O ) {
00737     // verifica que las dos matrices sean del mismo tamaño
00738     assert( "Sparse_Matrix<E>::substract()" && (rows() == O.rows()) && (cols() == O.cols()) );
00739 
00740     // crea una nueva matriz que va a ser la que se devuelve
00741     reSize(O.m_rows, O.m_cols);
00742 
00743     // Como las matrices son del mismo tamaño basta sumarlas entrada por entrada.
00744 
00745     value_type *pThis = m_val;
00746     value_type *pO    = & O.m_val[0];
00747     value_type *pEND  = &m_val[m_cols * m_rows];
00748     for ( ; pThis != pEND; ++pThis, ++pO) {
00749         *pThis -= *pO;
00750     }
00751     return;
00752 }
00753 
00754 /** Calcula la multiplicación <code> A * B </code> y la almacena en \c "*this".
00755     - Las dimensiones de \c "*this" se ajustan de manera que:
00756       - <code> rows() == A.rows() && cols() == B.cols()</code>
00757 
00758     \pre
00759     - \c "A" y \c "B" deben tener dimensiones compatibles
00760     - <code> A.cols() == B.rows() </code>
00761     - La multiplicación se hace [Fila x Columna], lo que implica que la cantidad
00762       de valores en la filas de \c "A" debe ser igual a la cantidad de columnas de \c "B"
00763 
00764     \par Complejidad:
00765          O( <code> A.cols() * B.cols() * A.cols() * A.capacity() * B.capacity() </code> )
00766 
00767     \remarks
00768     - Esta es la implementación de Sparse_Matrix<E> operator*( Sparse_Matrix<E>&, Sparse_Matrix<E> )
00769     - El compilador tiene problemas en compilar un función amiga (<em>"friend"</em>)
00770       definida con plantillas si esa función amiga no está definida (implementada)
00771       dentro de la declaración de la clase. Para solventar esta deficiencia existe
00772       este método que realiza el trabajo, aunque es poco cómodo de usar.
00773 */
00774 template <class E>
00775 void Sparse_Matrix<E>::multiply( const Sparse_Matrix<E> & A, const Sparse_Matrix<E> & B ) {
00776     // Verifica que las matrices se puedan multiplicar
00777     assert( (A.cols() == B.rows()) && " => Sparse_Matrix<E>::multiply()" );
00778 
00779     reSize( A.rows(), B.cols() );
00780     value_type sum;
00781     for (unsigned i=0; i<rows(); i++) {
00782         for (unsigned j=0; j<cols(); j++) {
00783             sum = 0; // sum = E(0);
00784             for (unsigned k=0; k<A.cols(); k++) {
00785                 sum = sum + A(i,k) * B(k,j);
00786             }
00787             // this->(i,j) = sum; // produce un error de compilación
00788             // this->operator()(i,j) = sum; // también funciona
00789             (*this)(i,j) = sum; // también funciona
00790         }
00791     }
00792     return;
00793 }
00794 
00795 /// Graba en el flujo \c COUT el valor de \c M[][].
00796 template <class E>
00797 std::ostream& operator<<(std::ostream& COUT, const Sparse_Matrix<E>& M) {
00798     COUT << '[' << M.rows() << 'x' << M.cols() << ']' << std::endl;
00799     for (unsigned i=0; i < M.rows(); ++i) {
00800         for (unsigned j=0; j < M.cols(); ++j) {
00801             COUT << "  " << M(i,j);
00802         }
00803         COUT << std::endl;
00804     }
00805     return COUT;
00806 }
00807 
00808 /// Obtiene del flujo \c CIN el valor para \c M[][].
00809 template <class E>
00810 std::istream& operator>>(std::istream& CIN, Sparse_Matrix<E>& M) {
00811     assert( "This code has not been tested" );
00812     unsigned rows,cols;
00813     CIN >> rows >> cols;
00814     M.reSize(rows,cols);
00815     for (unsigned i=0; i<rows; i++) {
00816         for (unsigned j=0; j<cols; j++) {
00817             CIN >> M(i,j);
00818         }
00819     }
00820     return CIN;
00821 }
00822 
00823 } // namespace Mx
00824 
00825 // Este [horrible] truco permite "compartir" el mismo archvio Matrix_Lib.h
00826 // entre Matrix.h y Sparse_Matrix.h
00827 // #define   Matrix Sparse_Matrix
00828 // #include "Matrix_Lib.h"
00829 // #undef    Matrix
00830 
00831 #include "Matrix_Lib.h"
00832 
00833 #endif // Sparse_Matrix_h
00834 // EOF: Sparse_Matrix.h

Generado el Wed Nov 14 13:47:09 2007 para Uso de Mx::Matrix: por  doxygen 1.3.9.1