// Programación en C++ para Ingenieros, Ed. Thomson Paraninfo, 2006
// Capítulo 5: Subprogramas: Acciones y funciones


#include <iostream>
#include <math.h>

using namespace std;

// Lee un vertice (generico)
template <class T>
void leerVertice(T& coordX, T& coordY);


// Escribe los 4 valores de la caja englobante (generico)
template <class T>
void escribeCaja(T& coordX, T& coordY, T& coordXFin,
                 T& coordYFin);

// Determina si se repiten dos vertices de enteros
bool heAcabado(int x0, int y0, int xFin, int yFin);

// Determina si se repiten dos vertices de reales
bool heAcabado(double x0, double y0, double xFin, double yFin);

// Devuelve el minimo de dos elementos
template <class T>
T minimo(T x, T y);

// Devuelve el maximo de dos elementos
template <class T>
T maximo(T x, T y);

// Calcula la caja englobante (generico)
template <class T>
void cajaEnglobante(T& coordX0, T& coordY0, T& coordXFin,
                    T& coordYFin);



int main(void)
{
      char c;

      // El usuario selecciona el tipo de vertices (validacion)
      do
      {
            cout << "¿Vertices reales (R) o enteros (E)? " << endl;
            cin >> c;
      }
      while (c != 'r' && c != 'R' && c != 'e' && c != 'E');

      // Se llama a la funcion correspondiente
      switch (c)
      {
      case 'r':
      case 'R':
            {
                  cout << "Leyendo reales" << endl;

                  double xMinR, xMaxR, yMinR, yMaxR;
                  cajaEnglobante(xMinR, xMaxR, yMinR, yMaxR);
                  escribeCaja(xMinR, xMaxR, yMinR, yMaxR);
            }
            break;
      case 'e':
      case 'E':
            {
                  cout << "Leyendo enteros " << endl;

                  int xMin, xMax, yMin, yMax;
                  cajaEnglobante(xMin, xMax, yMin, yMax);
                  escribeCaja(xMin, xMax, yMin, yMax);
            }
            break;
      }
      return 0;
}

// Funcion generica que lee un vertice
template <class T>
void leerVertice(T& coordX, T& coordY)
{
      cout << "Entra el siguiente vertice: ";
      cin >> coordX >> coordY;

}

// Funcion que determina si dos vertices de enteros son iguales
bool heAcabado(int x0, int y0, int xFin, int yFin)
{
      return (x0 == xFin && y0 == yFin);
}

// Funcion que determina si dos vertices de reales son iguales
// Notese que se utiliza un esquema ligeramente diferente porque
// dos reales supuestamente iguales se pueden almacenar con algun
// decimal diferente en funcion de la precision, aqui asumimos que
// dos reales con una diferencia menor de 0.000001 son iguales
bool heAcabado(double x0, double y0, double xFin, double yFin)
{
      const double precision = 0.000001;

      return (fabs(x0 - xFin) < precision
            && fabs(y0 - yFin) < precision);
}

// Calcula la caja englobante (generico)
template <class T>
void cajaEnglobante(T& coordX0, T& coordY0, T& coordXFin,
                              T& coordYFin)
{
      T x, y, xIni, yIni;
      leerVertice(xIni, yIni);
      coordX0 = coordXFin = xIni;
      coordY0 = coordYFin = yIni;

      leerVertice(x, y);

      while (!heAcabado(xIni, yIni, x, y))
      {
            coordX0 = minimo(x, coordX0);
            coordY0 = minimo(y, coordY0);
            coordXFin = maximo(x, coordXFin);
            coordYFin = maximo(y, coordYFin);

            leerVertice(x, y);
      }
}

// Calcula el minimo de dos elementos
template <class T>
T minimo(T x, T y)
{
      T aux = x;

      if (y < aux)
            aux = y;

      return aux;
}

// Calcula el maximo de dos elementos
template <class T>
T maximo(T x, T y)
{
      T aux = x;

      if (y > aux)
            aux = y;

      return aux;
}


// Escribe los componentes de una caja englobante
template <class T>
void escribeCaja(T& coordX, T& coordY, T& coordXFin,
                         T& coordYFin)
{
      cout.precision(5);
      cout << "La caja englobante va de "
             << coordX << " " << coordY << " a "
             << coordXFin << " " << coordYFin << endl;
}