/** @(#) mathpos/array2dpos.h */
#ifndef ARRAY2DPOS_H
#define ARRAY2DPOS_H
//----------------------------------------------------------------------------
#include <iostream>
#include <string>
#include <stdexcept>	// runtime_exception
using namespace std;

/**
* Reservar memoria.
* No hace operaciones aritmeticas.
* Entrada y salida de arreglos.
* @author Omar Posada Villarreal
* @version 1.1, 24/04/2002
* @version 1.0, 13/04/2002
*/
template <class TC>
class Array2DPos {

//----------------------------------------------------------------------------
private:
	/** Apuntador a [firstRow][firstCol]. */
	TC **pCorner;
	TC **pCenterCol;

	/** Apuntador a [0][0]. */
	TC **pOrigin;

	/** Numero de renglones. */
	int row;
	int col;

	/** Indice del primer renglon */
	int firstRow;
	int firstCol;
	int lastRow;
	int lastCol;

//----------------------------------------------------------------------------
public:

// constructor, ~, friend-----------------------------------------------------
/**
* Reserva memoria para un arreglo bidimensional que permite indices negativos.
* DUDA Si firstRow, firstCol de signos contrarios, 
* asignacion sin implementar
* Uso:
* 	Array2DPos<double> M(n, n);
* @param theFirstRow [-N, +N] theFirstRow <= theLastRow
* @param theFirstCol [-N, +N] theFirstCol <= theLastCol
* @param theLastRow [-N, +N]
* @param theLastCol [-N, +N] */
// templates solo en *.h
Array2DPos(int theFirstRow, int theLastRow, int theFirstCol, int theLastCol)
		throw (invalid_argument){
	// valida
	if ((theFirstRow > theLastRow) || (theFirstCol > theLastCol)){
		throw invalid_argument("Array2DPos: indices invertidos");
	}
	firstRow = theFirstRow;
	firstCol = theFirstCol;
	lastRow = theLastRow;
	lastCol = theLastCol;

	// Num de reng y col
	row = lastRow - firstRow + 1;
	col = lastCol - firstCol + 1;

	// pCorner
	pCorner = new TC*[row];
	int i;
	for (i = 0; i < row; ++i) {
		pCorner[i] = new TC[col];
	}

	// pOrigin
	pCenterCol = new TC*[row];
	for (i = 0; i < row; ++i) {
		pCenterCol[i] = &(pCorner[i][ -firstCol]);
	}
	pOrigin = &(pCenterCol[ -firstRow]);
	// IMPORTANTE Previene underflow del apuntador pOrigin 
	// si first > 0, pOrigin < pCorner
	if ((firstRow > 0) && (firstCol > 0)) {
		if (pOrigin > pCorner) {
			throw runtime_exception(
				"Array2DPos: apuntador ArrayOrigin negativo.");
		}
	}
	// DUDA Si firstRow, firstCol de signos contrarios, 
	// asignacion sin implementar
	if ( (firstRow * firstCol) < 0 ) {
		throw runtime_exception("Array2DPos: falta implementar.");
	}
}

Array2DPos(int theRows, int theColumns) throw (invalid_argument) {
	if ((theRows < 1) || (theColumns < 1)) {
		throw invalid_argument("Array2DPos: indices negativos");
	}
	Array2DPos(0, theRows - 1, 0, theColumns - 1);
}

/**
* Libera memoria de un arreglo bidimensional de double.
* @param row Numero de renglones.
*/
// templates solo en *.h
~Array2DPos() {
	deleteArray();
}

/** Libera memoria. */
void deleteArray() {
	delete[] pOrigin;
	delete[] pCenterCol;

	int i;
	for (i = 0; i < row; ++i) {
		delete[] pCorner[i];
	}
 	delete []pCorner;
	//cout << "} ~Array2DPos()";
}

// inline---------------------------------------------------------------------
inline TC **getArrayOrigin() const {
	return pOrigin;
}
inline int getRows() const {
	return row;
}
inline int getColumns() const {
	return col;
}
inline int getFirstRow() const {
	return firstRow;
}
inline int getLastRow() const {
	return lastRow;
}
inline int getFirstColumn() const {
	return firstCol;
}
inline int getLastColumn() const {
	return lastCol;
}

/** Abreviaciones */
inline int gR() const {	return row; }
inline int gC() const {	return col; }
inline int gFR() const {	return firstRow; }
inline int gLR() const {	return lastRow; }
inline int gFC() const {	return firstCol; }
inline int gLC() const {	return lastCol; }

// operator-------------------------------------------------------------------
/** Checa indices. */
TC operator()(int indRow, int indCol) throw (invalid_argument) {
	if ( (indRow < firstRow) || (indRow > lastRow)
			|| (indCol < firstCol) || (indCol > lastCol) ) {
		throw invalid_argument("operator(): indices fuera de rango.");
	}
	return pOrigin[indRow][indCol];
}

// public---------------------------------------------------------------------
/** Muestra en salida estandar los elementos. Separados. Col:\t, Reng:\n. */
void print() {
	int i, j;
	for (i = 0; i < row; ++i) {
		for (j = 0; j < col; ++j) {
			cout << pCorner[i][j] << "\t";
		}
		cout << endl;
	}
}

/** Asigna ceros a todos los elementos. */
void clean() {
	int i, j;
	for (i = 0; i < row; ++i) {
		for (j = 0; j < col; ++j) {
			pCorner[i][j] = static_cast<TC>(0);
		}
	}
}

}; // } Array2DPos------------------------------------------------------------
#endif
// Fin------------------------------------------------------------------------