/** @(#) matrixdouble/rectangularmatrix.cpp */
/**
* Define las funciones del encabezado.
* @author Daniel Alba Cuellar, Omar Posada Villarreal
* @version 1.0, 27/02/2002
*/
#include "rectangularmatrix.h"

// constructor, ~, friend-----------------------------------------------------
/**
* Construye una matrix rectangular de nr x nc.
* Reserva memoria.
* @param cleanMe true: Limpia la matriz con 0.0. false: no inicializa.
*/
RectangularMatrix :: RectangularMatrix(
		int theRows, int theColumns, int theLogic, bool cleanMe)
		: MatrixDouble(theRows, theColumns, theLogic) {
	m = NULL;
        resize(theRows, theColumns, theLogic, cleanMe);
}

/** Libera recursos. */
RectangularMatrix :: ~RectangularMatrix() {
	deleteRectangular();
	//No usar delete[] m;
}

/** Usado por destructor y resize. */
void RectangularMatrix :: deleteRectangular() {
	int i;
        // No llamar al destructor, error por acceder a m[i]
        // DestructorVectorDouble *pRow; pRow = &(m[i]); delete pRow;
        for (i = 0; i < rows; ++i) {
        	m[i].deleteArray(); //solo libera memo del arreglo
        }
        if (m != NULL) {
        	delete[] m;
	        // No usar m = NULL;
        }

}
/*
template<class T>
void Matriz<T>::liberaMemoria()
{
	 for(int i=0; i<m_renglones; i++){
	 	  delete[] m_matriz[i].m_vector;
	 	  m_matriz[i].m_vector=NULL;
	 }

	 if(m_matriz!=NULL)
	  	delete [] m_matriz;

	 m_matriz=NULL;
}*/


/** Reconstruye todos los parametros de la matriz.
* Libera y asigna memoria. Se pierden los datos.
* Si es de misma logica, no cambia.
* Si es del mismo taman~o, no cambia.
* Logica cero. */
void RectangularMatrix :: resize(
		int theRows, int theColumns, int theLogic, bool cleanMe) {
/* algort
        resize(theRows, theColumns);	// 1. Mantener orden
	setLogic(theLogic);		// 2. Necesita memoria
	if (cleanMe) {			// 3. Necesita logica
        	clean();
	}
*/
	int validRows =	((theRows < 1) ? 1 : theRows);
	int validColumns =	((theColumns < 1) ? 1 : theColumns);
	logic = (theLogic ? 1 : 0);
        // Si es 1a creacion
//ERROR EN LA 2A CORRIDA
        if (m == NULL) {	// 1o.
        	m = new VectorDouble[validRows];
        // Si es de nueva creacion, se debe salir. Se debe duplicar codigo
        } else if (rows != validRows) {
        	deleteRectangular();
                // Error si se trata de llamar al constructor
        	m = new VectorDouble[validRows];

        }
        // Si el numero de columna no varia, lo checa VectorDouble
	int i;
        for (i = 0; i < validRows; ++i) {
        	m[i].resize(validColumns, logic, cleanMe);
                // no usar new VectorDouble(columns, logic, cleanMe);
        }
        rows = validRows;
        columns = validColumns;
}

// operator-------------------------------------------------------------------

/**
* Muestra la matriz en la consola. No muestra las dimensiones.
* Logica variable.
* @param outStr Destino del flujo.
* @param matrix Matriz a mostrar.
* @return Matriz
* @version usa << de vector
*/
ostream & operator<<(ostream &outStr, const RectangularMatrix &matrix) {

	//matrix.print();	// sig vers outStr <<
	int i;
        // Ajuste a la logica
        int first = matrix.getLogic();
        int nr = matrix.getRows() + first;

	//* @version usa << de vector
	for (i = first; i < nr; ++i) {
        	outStr << matrix[i] << endl;
	}
        return outStr;
}

/**
* Entrada de la matriz en la consola.
* Permite lectura de archivo de las dimensiones y los datos.
* No se lee la logica de la matriz.
* Logica variable.
* Uso:
* 2 3
* 1 2 3
* 4 5 6
* @param outStr Destino del flujo.
* @param matrix Matriz a mostrar.
* @return Matriz
*/
istream & operator>>(istream &inpStr, RectangularMatrix &matrix) {

	//matrix.read();	// sig inpStr >>
        int i, j;
        int nr, nc;
        double value;

        inpStr >> nr;
        inpStr >> nc;
        int first = matrix.getLogic();
        matrix.resize(nr, nc, first, true);	// first == logic

        // Ajuste a la logica
        nr += first;
        nc += first;

	for (i = first; i < nr; ++i) {
		for (j = first; j < nc; ++j) {
                	inpStr >> value;
                        matrix[i][j] = value;
                        // error si inpStr >> matrix(i, j)
		}
	}
        return inpStr;
}

/**
* Permite asignacion (operator=).
* Logica 0, (0, 0) apunta al matematico A 1,1 esq sup izq
* @param row Logica cero: [0, row -1]. Logica uno: [1, row]
* @param column Logica cero: [0, col -1]. Logica uno: [1, col]
*/
double & RectangularMatrix :: operator()(int row, int column)
		throw (invalid_argument) {
	if ( (row < logic) || (row >= (rows + logic))
        		|| (column < logic)
                        || (column >= (columns + logic)) ) {
        	throw invalid_argument("operator(): indices fuera de rango.");
        }
	return m[row - logic][column];
}

/** @version const Sino error por rvalue. */
const double & RectangularMatrix :: operator()(int row, int column) const
		throw (invalid_argument) {
	if ( (row < logic) || (row >= (rows + logic))
        		|| (column < logic)
                        || (column >= (columns + logic)) ) {
        	throw invalid_argument("operator(): indices fuera de rango.");
        }
	return m[row - logic][column];
}

/**
* Multiplica la matriz actual por un vector columna
* (arreglo unidimensional de "double")
* y crea un vector columna "b" de taman~o m para
* almacenar el resultado a partir de la dimension de A.
* Por default se refiere a postmultiplicacion.
* Es decir, se multiplica la matriz por un vector columna.
* Este metodo puede ser sobreescrito ("override") para eficientarlo
* ya que recorre todos los elementos (existan o no) de la matriz.
* Las dimensiones de las matrices deben ser
* A(n x m) b(m x 1) = c(m x 1).
* Ejemplo: <p><code>
* arrDoubC = mdA.multiplyBy(arrDoubB)
* </code></p>
* No checa dimensiones, ni logica
* Logica cero.
* @return Vector columna (double[]) producto entre
* "MatrixDouble" y el vector columna "b".
* "null" si las dimensiones no coinciden.
* @see #premultiply
*/
//Nota: Debe ser sobreescrita para eficientar
VectorDouble RectangularMatrix :: operator*(VectorDouble &left) {
	/* Serge Lang.
    	* A n*m b m*1 = C n*1
        * c [i] = Sum(j = 1, m , aij bj)
	*/
        left.setLogic(logic);
	VectorDouble	res(rows, logic);

	int	i, j; 		//contadores
	double 	sum;
        //double mi, lj;
	/*Barre matriz de izq a der, arriba a abajo*/
	for (i = 0; i < rows; i++) {
		sum = 0.0;
		for (j = 0; j < columns; j++) {
                      	//mi = m[i][j + logic];
                        //lj =left[j + logic];
			sum += m[i][j + logic] * left[j + logic];
		}
		res[i + logic] = sum;
	} // for i
    	return res;	// Resultado
}

/**
* Multiplica la matriz actual por un escalar
* y crea una matriz cuadrada del mismo taman~o
* Ejemplo:
* No checa dimensiones, ni logica
* Logica cero.
* @return RectangularMatrix
*/
//Nota: Debe ser sobreescrita para eficientar
// No se puede regresar RectangularMatrix error del destructor
//RectangularMatrix RectangularMatrix :: operator*(double left) {
// static
void RectangularMatrix :: multiply(
		RectangularMatrix &right, double left,
                RectangularMatrix &res)
        	throw (runtime_error) {
	// *RectangularMatrix	res(rows, columns, logic);
        bool	logic = right.getLogic();
        int	rightRow = right.getRows();
        int	rightCol = right.getColumns();	// == left.Row

	int	i, j; 		//contadores

	/*Barre matriz de izq a der, arriba a abajo*/
        try {
	for (i = 0; i < rightRow; i++) {
		for (j = 0; j < rightCol; j++) {
			res.m[i][j + logic] = right.m[i][j + logic] * left;
		}
	} // for i
        } catch(...) {
        	throw runtime_error("multiply: Desbordamiento float");
        }
    	//return res;
}

/** Multiplica la matriz actual por la del argumento.
* Las dimensiones de las matrices deben ser
* A(n x m) B(m x p) = c(n x p).
* Se deja la logica igual para todos.
* No checa dimension m.
* Logica cero.
* @return Matriz de dimensiones rows x left.columns
*/
// No se puede regresar RectangularMatrix error del destructor
//RectangularMatrix RectangularMatrix :: operator*(RectangularMatrix &left) {
// static
void RectangularMatrix :: multiply(
		RectangularMatrix &right, RectangularMatrix &left,
                RectangularMatrix &res) {
	/* Serge Lang.
    	* A n*m b m*p = C n*p
        * c [i] = Sum(j = 1, m , aij bj)
	*/
        bool	logic = right.getLogic();
        int	rightRow = right.getRows();
        int	rightCol = right.getColumns();	// == left.Row
        int	leftCol = left.getColumns();

        right.setLogic(logic);
        left.setLogic(logic);
	res.resize(rightRow, leftCol, logic, false);

	int	i, j, k;
	double 	sum;

        /*Barre matriz de izq a der, arriba a abajo*/
        for (i = 0; i < rightRow; i++) {
                for (k = 0; k < leftCol; k++) {
                        /*Para cada elemento aij*/
                        sum = 0.0;
                        for (j = 0; j < rightCol; j++) {
				// suma += aij * bjk
                                sum += right.m[i][j + logic] * left.m[j][k + logic];
                        }
                        // cik = suma;	// debug
                        res.m[i][k + logic] = sum;
                } //for k
        } // for i
        //return res;	// Resultado
}

/** Suma las matrices, regresa una nueva matriz con el resultado.
* No checa dimensiones, ni logica
* Logica cero.
* @return Matriz con las mismas caracteristicas que la matriz actual. */
// No se puede regresar RectangularMatrix error del destructor
//RectangularMatrix RectangularMatrix :: operator+(RectangularMatrix &left) {
// static
void RectangularMatrix :: add(
		RectangularMatrix &right, RectangularMatrix &left,
                RectangularMatrix &res) {
        bool	logic = right.getLogic();
        int	rightRow = right.getRows();
        int	rightCol = right.getColumns();

        right.setLogic(logic);
        left.setLogic(logic);
	res.resize(rightRow, rightCol, logic, false);

	int	i, j; 		//contadores

	for (i = 0; i < rightRow; i++) {
		for (j = 0; j < rightCol; j++) {
			res.m[i][j+logic] = right.m[i][j+logic] + left.m[i][j+logic];
		}
	} // for i
    	//return res;	// Resultado
}

/** Resta las matrices, regresa una nueva matriz con el resultado.
* Actual = right (operador) left.
* No checa dimensiones, ni logica
* Logica cero.
* @return Matriz con las mismas caracteristicas que la matriz actual. */
// No se puede regresar RectangularMatrix error del destructor
//RectangularMatrix RectangularMatrix :: operator-(RectangularMatrix &left) {
// static
void RectangularMatrix :: substract(
		RectangularMatrix &right, RectangularMatrix &left,
                RectangularMatrix &res) {
        bool	logic = right.getLogic();
        int	rightRow = right.getRows();
        int	rightCol = right.getColumns();

        left.setLogic(logic);
	res.resize(rightRow, rightCol, logic, false);
	//RectangularMatrix	res(rows, columns, logic, false);

	int	i, j; 		//contadores

	for (i = 0; i < rightRow; i++) {
		for (j = 0; j < rightCol; j++) {
			res.m[i][j+logic] = right.m[i][j+logic] - left.m[i][j+logic];
		}
	} // for i
    	//return res;	// Resultado
}

// public---------------------------------------------------------------------

/**
* Llena la matriz con el valor indicado.
* Logica cero.
* @author Omar Posada Villarreal
* @param value Cada elemento valdra este numero.
*/
void RectangularMatrix :: fill(double value) {
	int i;
	for (i = 0; i < rows; ++i) {
		m[i].fill(value);
	}
}

/** Muestra la matriz en la consola.
* Logica variable.
* @version usa << de vector. */
void RectangularMatrix :: print() const {
	int i;

        //cout << "Matriz (" << rows << " x " << columns << ")\n";
	for (i = 0; i < rows; ++i) {
        	//* @version usa << de vector
                cout << m[i] << endl;
	}
}

/**
* Muestra la matriz en la consola con un cierto numero de digitos.
* @param digits Numero de digitos a mostra (fixed).
*/
void RectangularMatrix :: print(int digits) const {
	cout << setiosflags(ios::fixed) << setprecision(digits);
	print();
}

/**
* Entrada de la matriz sin las dimensiones en la consola.
* Logica uno (row), variable(col).*/
void RectangularMatrix :: readElements() {
	int i, j;
        int nc = columns + logic ;

        cout << "\nTeclee elementos de la matriz ("
        	<< rows << " x " << columns << ")\n";
	for (i = 0; i < rows; ++i) {
		for (j = logic; j < nc; ++j) {
                	cout << "[" << (i+logic) << "][" << j << "]: ";
			cin >> m[i][j];
		}
	}
}

/**
* Cambia la logica de la matriz.
* Logica cero.
* @param theLogica 0: Logica cero. !=0: Logica 1.
*/
void RectangularMatrix :: setLogic(int theLogic) {
	int validLogic = (theLogic ? 1 : 0);
        // Si cambia la logica, cambia la de los renglones
        if (logic != validLogic) {
        	logic = validLogic;
	        int i;
        	for (i = 0; i < rows; ++i) {
        		m[i].setLogic(logic);
	        }
        }
}

/** Transpone una matriz. Se ajustan dimensiones de la actual */
void RectangularMatrix :: transpose(RectangularMatrix &matrix) {
	int	mRow = matrix.getRows();
	int	mCol = matrix.getColumns();
        int	mLogic = matrix.getLogic();

	if ((rows != mCol) || (columns !=mRow)) {
        	resize(mCol, mRow, mLogic, false);
        }
	int i, j;
        for (i = 0; i < mRow; ++i) {
        	for (j = 0; j < mCol; ++j) {
 			m[j].v[i] = matrix.m[i].v[j];
		}
        }
}

/** @return Vector con la diagonal con la misma logica y la dimension menor. */
VectorDouble RectangularMatrix :: takeDiagonal() {
	int n = min(rows, columns);
        VectorDouble diag(n, logic);
	int i;

	for (i = 0; i < n; i++) {
		diag[i + logic] = m[i][i + logic];
        }
        return diag;
}

/** Copia los datos a la matriz actual.
* No checa dimensiones. */
void RectangularMatrix :: copyFrom(RectangularMatrix &matrix) {
	int i, j;
        for (i = 0; i < rows; ++i) {
	        for (j = 0; j < columns; ++j) {
                        m[i][j + logic] = matrix.m[i][j + logic];
                }
        }
}

/**
* Resta dos matrices (A y B) y crea una de resultado.
* <p>C = A - B</p>
* No olvidar hacer un casting al resultado. Ejemplo de uso:
* <p><code>
* rmRes = (RectangularMatrix)MatrixDouble.substract(rmA, rmB);
* </code></p>
* Se recomienda que las tres matrices sean del mismo tipo
* para que no se pierdan elementos.
* La matriz C debe ser del mismo tipo que A.
* La matriz C se crea con las dimensiones y el tipo de
* de la matriz A.
* @throws IllegalArgumentException Matrices con dimensiones distintas.
* @return Matriz con el resultado.
*/
/*public static MatrixDouble substract(MatrixDouble mdA, MatrixDouble mdB) {
	int	rowsA = 	mdA.getRows();
	int	columnsA = 	mdA.getColumns();
	int	rowsB = 	mdB.getRows();
	int	columnsB = 	mdB.getColumns();
	MatrixDouble	mdC = 	null;	// Resultado
	int	i;
	int	j;

	if ( (rowsA != rowsB) || (columnsA != columnsB) ) {
		System.err.println("substract: "
			+ "Matrices con dimensiones distintas.");
		throw new IllegalArgumentException(
			"substract: "
			+ "Matrices con dimensiones distintas.");
	}

	// Crea una matriz de resultados fija.
	mdC = mdA.newMatrix(rowsA, columnsA,
		mdA.memoryRows, mdA.memoryColumns);
	for (i = 0; i < rowsA; i++) {
		for (j = 0; j < columnsA; j++) {
			mdC.setE(i, j,
				mdA.getE(i, j) - mdB.getE(i, j) );
		}
	}
	return (MatrixDouble)mdC;	//exito
} //-------------------------------------------------------------------
*/

// private--------------------------------------------------------------------
// Fin------------------------------------------------------------------------