// lab16.cpp (c) 2006 adolfo@di-mare.com

/** \file  lab16.cpp
    \brief Ejemplo de uso de matrices bidimensionales de tamaño fijo.

    \author Adolfo Di Mare <adolfo@di-mare.com>
    \date   2006
*/

/// Define el tipo \c Matrix como una matriz bidimensional.
/// - Las dimensiones máximas de la matriz son 8x8.
/// - Abreviatura para definir <code> int M[8][8]; </code>
typedef int Matrix[8][8];

#include <assert.h>   // assert()

/** Calcula la multiplicación <code> A * B </code> y la almacena en \c Res.
    - \c A es una matriz de dimensiones <code>A_Rows x A_Cols </code>.
    - \c B es una matriz de dimensiones <code>B_Rows x B_Cols </code>.
    - Las dimensiones de \c Res después de la multipliación son
      <code> A_Rows x B_Cols </code>.

    \pre
    - \c "A" y \c "B" deben tener dimensiones compatibles.
    - <code> A_Cols == B_Rows </code>.
    - La multiplicación se hace [Fila x Columna], lo que implica que la
      cantidad de valores en la columnas de \c "A" debe ser igual a la
      cantidad de filas de \c "B".

    \par Complejidad:
         O( <code> A_Cols * B_Cols * A_Cols </code> )
*/
void Multiplique( int Res[8][8], // sin el "typedef"
     const Matrix A, unsigned A_Rows, unsigned A_Cols,
     const Matrix B, unsigned B_Rows, unsigned B_Cols
) {
    assert( A_Cols == B_Rows );

    for (unsigned i=0; i<A_Rows; i++) {
        for (unsigned j=0; j<B_Cols; j++) {
            int sum = 0;
            for (unsigned k=0; k<A_Cols; k++) {
                sum = sum + A[i][k] * B[k][j]; // A[i,k] ==> error !!!
            }
            Res[i][j] = sum; // también funciona
        }
    }
    return;
}

#include <iostream.h> // cout
#include <iomanip.h> // setw()

/// Graba en \c COUT por filas el valor de la matriz \c "M".
/// - \c M es una matriz de dimensiones <code> M_Rows x M_Cols </code>.
void Grabador( ostream & COUT, const char* name,
    const Matrix M, unsigned M_Rows, unsigned M_Cols
) {
    cout << endl << name << '[' << M_Rows << ',' << M_Cols << ']' << endl;
    for (unsigned i=0; i < M_Rows; ++i) {
        for (unsigned j=0; j < M_Cols; ++j) {
            COUT << "  " << setw(3) << M[i][j];
        }
        COUT << endl;
    }
}


#include <stdlib.h>   // srand() && rand()


/** Efectúa esta multiplicación matricial Res = A x B:
\code
           A[3,4]                  B[4,5]
      /               \     /        ___        \
      | 1   8   0   0 |     | 0   1 | 1 | 5   0 |
  [1]_|_0___9___9___1_|  X  | 0   3 | 5 | 8   7 |
      | 5   6   9   6 |     | 6   0 | 2 | 4   6 |
      \               /     | 0   8 |_0_| 0   0 |
                            \        [2]        /

                Resultado Res[3,5]
              /                    \
           0  |  0  25  41  69  56 |
     ===> [1] | 54  35  63 108 117 |
           2  | 54  71  53 109  96 |
              \          |         /
                 0   1  [2]  3   4

      Res[1][2] == 0*1 + 9*5 + 9*2 + 1*0 == 63
\endcode
*/
int main()  {
    Matrix A,B, Res;
    int i,j;
    unsigned A_Rows = 3 , A_Cols = 4;
    unsigned B_Rows = 4 , B_Cols = 5;

    srand( 1492 ); // inicializa la semilla aleatoria

    for (i=0; i < A_Rows; i++) {
        for (j=0; j < A_Cols; j++) {
            A[i][j] = ( (rand()%4 > 0) ? rand() % 10 : 0 );
        }                   // 25% de ceros en la matriz
    }
    Grabador( cout, "A", A, A_Rows, A_Cols );

    for (i=0; i < B_Rows; i++) {
        for (j=0; j < B_Cols; j++) {
            B[i][j] = ( (rand()%4 > 0) ? rand() % 10 : 0 );
        }                   // 25% de ceros en la matriz
    }
    Grabador( cout, "B", B, B_Rows, B_Cols );

    Multiplique( Res,
        A, A_Rows, A_Cols,
        B, B_Rows, B_Cols
    );
    Grabador( cout, "Resultado Res", Res, A_Rows, B_Cols );

    return 0;
}

// EOF: lab16.cpp