/** @(#) doglegdriver.cpp */

// #include "ap.h"
/** @(#) .h */
/**
* @author Daniel Alba Cuellar
* @author Omar Posada Villarreal
* @version 1.0, /03/2002
*/
#ifndef DOGLEGDRIVER_H
#define DOGLEGDRIVER_H
//----------------------------------------------------------------------------
#include "doglegstep.cpp"
#include "updatetrustregion.cpp"
#include "../functionnd/functionnd_1d.h"
#include "../mathpos/mathpos.h"
#include "../matrixdouble/squaredmatrix.h"
#include "../matrixdouble/vectordouble.h"
#include <iomanip>
#include <iostream>
#include <string>
#include <stdexcept>	// runtime_exception
using namespace std;

/** Busca una x+ en la curva double dogleg tal que
* f(x+) <= f(xc) + ag^T (x+ - xc)
* (se usa alfa = 10^-4) y un taman~o de paso escalado steplength = delta,
* iniciando con la entrada delta pero incrementando o decrementando
* delta si es necesario. Tambien, produce una region de confianza
* inicial delta para la siguiente iteracion.
* Se llama al algoritmo A6.4.4. y A6.4.5
* A6.4.4. para el taman~o de paso.
* A6.4.5. Aceptar
* Usa logica uno.
* @author Omar Posada Villarreal, Daniel Alba Cuellar
* @param n Taman~o de vectores y matrices. No se checan dimensiones.
* @param xc Vector con punto de prueba inicial. De aqui se toma la dimension
* @param fc Evaluacion de la funcion en xc.
* @param theFunction Nombre de la funcion. F:R^n->R. FN.
* @param g Gradiente
* @param L Triangular inferior
* @param sN = - (L L^T)^-1 g). Neton step.
* @param Sx Escala. Valores usuales.
* @param maxstep Maximo taman~o de paso. Dado por QuasiNewton
* @param steptol Precision del paso
* @param delta in/out Radio de la region de confianza.
* @param retcode return
*	0: x+ satisfactorio encontrado
*	1: la rutina fallo para encontrar una x+ satisfactoria,
*	suficientemente distinta de xc.
* @param xp return x+
* @param fp return f+
* @param maxtaken return
* @param Newttaken return Este parametro es solo informativo,
* no vienen en Dennis, se usa para escribir en archivo.
*	true: Se usa el paso de Newton
*	false: se usa el dogleg step
* @version 1.0, 20/04/2002, sig vers LowerTriangularMatrix &L
* @see Dennis. A6.4.3. DOGDRIVER pagina 335
*/
void doubleDogLegDriver(int n, VectorDouble &xc, double fc,
		FunctionND_1D &theFunction, VectorDouble &g, SquaredMatrix &L,
                VectorDouble &sN, VectorDouble &Sx,
                double maxstep, double steptol,
                double &delta,
                int &retcode, VectorDouble &xp, double &fp, bool &maxtaken,
                bool &Newttaken,
                int logType = SHORT_LOG, int waitType = NO_WAIT)
                throw (invalid_argument) {
	LLOG << "\n{ doubleDogLegDriver";
        // didac
	LLOG << "\n-----IN-----";
	LLOG << "\nDimension. n = " << n;
        LLOG << "\nPunto inicial. xc: " << xc;
        LLOG << "\nf(xc). Funcion en el punto inicial. fc: " << fc;
	LLOG << "\ngrad(f(xc)). g = " << g;
        LLOG << "\nTriangular inferior del Hessiano. L:\n" << L;
        LLOG << "\nsc^N = -Hc^-1 grad(f(xc)). sN:" << sN;
        LLOG << "\nEscala metrica. Sx: " << Sx;
	LLOG << "\nMaxima delta. maxstep = " << maxstep;
	LLOG << "\nsteptol = " << steptol;
	LLOG << "\n-----IN/OUT ANTES-----";
	LLOG << "\ndelta c. Radio de la region de confianza. delta = "<< delta;
	LWAIT();

	// Misma logica
	xc.setLogic(1);
	g.setLogic(1);
	L.setLogic(1);
	sN.setLogic(1);
	Sx.setLogic(1);
	xp.setLogic(1);

        // Var de A6.4.3

	// Var de A6.4.4
          // in
	double	Newtlen;	// init por 3.
          // in/out
        bool	firstdog;
        double	Cauchylen;
        double	eta;
	VectorDouble	sSD(n, 1);
        VectorDouble	v(n, 1);
          // out
        VectorDouble	s(n, 1);
        //bool	Newttaken;	// Si no se usa param, descomentar

        // Var de A6.4.5
	// Usado por A6.4.4. VectorDouble &s,
        // Usado por A6.4.4. bool Newtakken,
        int steptype;
	SquaredMatrix H(n, 1);	// Dummy, usado para hookstep
	VectorDouble	xpprev(n, 1);
	double	fpprev;		// ??? init
	VectorDouble	Dx_sN(n, 1);

	// Es una matriz cuadrada, pero se almacenara como un vector a Sx
	VectorDouble	Dx(Sx);

	// Significa una llamada inicial a A6.4.4
        steptype = 2;			// 0. Usar a dogstep

// A6.4.5.
	retcode = 4;			// 1.
        firstdog = true;		// 2.
        // ||Dx sN||2
	// Dx = diag((Sx)1, ... ,(Sx)n)
	// se almacena como Sx E R^n..
	// Para implementacion sugerida de expresiones
	// que conciernen a Dx, ver seccion 1.5 del apendice
        				// 3.
        Dx_sN = Dx.diagonalProduct(sN);
        Newtlen = Dx_sN.norm2();

        // Calcula y checa un nuevo paso
        do {				// 4.

        	// Encuentra el nuevo paso con el algoritmo double dogleg
                // A.6.4.4
        	// respetando la declaracion
                LWAIT();
                LLOG << "\n------------------------------";
		doubleDogLegStep(n, g, L,
                	sN, Sx, Newtlen,
                	maxstep,
                        delta, firstdog,
                        Cauchylen, eta, sSD,
                        v,
        		s, Newttaken);	// 4.2
                // Checa un nuevo punto y actualiza el radio
                // de confianza A6.4.5.
                // La segunda L en la lista de parametros es dummy
		// test s[1] = -0.334;
		// test s[2] = -0.335;
		LLOG << "\nSe uso el paso de "
                	<< (Newttaken ? "Newton" : "Dogleg");

		LWAIT();
                LLOG << "\n------------------------------";
		updateTrustRegion(n, xc, fc,
			theFunction, g, L,
                	s, Sx, Newttaken,
                	maxstep, steptol, steptype,
	                H,
                	delta, retcode, xpprev,
	                fpprev,
                	xp, fp, maxtaken);	// 4.3.
                LLOG << "\ndelta = " << delta;
/*void updateTrustRegion(int n, VectorDouble &xc, double fc,
		FunctionND_1D &theFunction, VectorDouble &g, SquaredMatrix &L,
                VectorDouble &s, VectorDouble &Sx, bool Newttaken,
                double maxstep, double steptol, int steptype,
                SquaredMatrix &H,
                double &delta, int &retcode, VectorDouble &xpprev,
                double &fpprev,
                VectorDouble &xp, double &fp, bool &maxtaken) {
*/

        } while (retcode > 1);	// 4U. until retcode < 2

        // didac
	LLOG << "\n-----IN/OUT DESPUES-----";
	LLOG << "\ndelta c. Radio de la region de confianza. delta = "<< delta;
	LLOG << "\n-----OUT-----";
	LLOG << "\nBandera de retorno. 0: x+ satisf., 1: x+ no satisf. "
        	<< "\nretcode = " << retcode;
	LLOG << "\nxp = " << xp;
	LLOG << "\nfp = " << fp;
	LLOG << "\nmaxtaken = " << maxtaken;
	LWAIT();

	LLOG << "\n  } doubleDogLegDriver";
}
//----------------------------------------------------------------------------
#endif
/*
  try {
	// Presentacion
	cout << "OptDogLeg. "
        	<< "Daniel Alba Cuellar, Omar Posada Villarreal";

        // Entrada
	cout << "\nSe usara logica 1.";
        cout << "\nLa funcion vectorial es de dimension 2.";

        // Var de entrada
        int n = 2; 	//dimension
	double fc;	// escalar
        VectorDouble 	xc(n, 1);
        VectorDouble 	Sx(n, 1);
        SquaredMatrix 	H(n, 1);
        Function1 	func;

/* Comentar al probar
        cout << "\nPunto inicial Ejemplo: 1 1. xc: ";
        xc.readElements();
        cout << xc;

        cout << "\nEscala Ejemplo: 1 1. Sx: ";
        Sx.readElements();
        cout << Sx;
/**/
/*

	// Proceso
        VectorDouble g(n, 1);
        SquaredMatrix L(n, 1);
        VectorDouble sN(n, 1);

        //VectorDouble Sx(n, 1);
	double maxstep;
        double delta;

	// Var doubleDogLegDriver
        double	steptol;
        int	retcode;
        VectorDouble	xp(n, 1);
        double	fp;
        bool	maxtaken;

        /*
        // Var solo de doubleDogLegStep()
        double Newtlen;
        bool firstdog;
        double Cauchylen;
        double eta;
        VectorDouble sSD(n, 1);
        VectorDouble v(n, 1);
        VectorDouble s(n, 1);
        bool Newttaken;
	*/

/*
        SquaredMatrix LT_L(n, 1);
        SquaredMatrix LT_LInv(n, 1);

/* Comentar al entregar
        /*
        xc = [ 1	1 	]
        H(xc) =
        [ 14	0	]
	[ 0	2	]

        H^-1(xc) =
        [ 0.0714286	0	]
        [ 0		0.5	]
        */
/*	xc[1] = 1;
	xc[2] = 1;

        Sx[1] = 1;
        Sx[2] = 1;

        H[1][1] = 14;
        H[1][2] = 0;
        H[2][1] = 0;
        H[2][2] = 2;

	if (!factorizeCholesky(n, H, L) ) {
        	throw runtime_error("No se pudo factorizar.");
        }
        g[1] = 6.0;
        g[2] = 2.0;

        //LT_L = L.transpose() * L;
        LT_LInv[1][1] = 1.0/14.0;
        LT_LInv[2][2] = 1.0/2.0;
        //error sN = (LT_LInv * g) * (-1.0);
        /* doglegstep*/
/*        sN[1] = -0.428571;
        sN[2] = -1.0;
        /**/
        /* driver
        sN[1] = 0.334;
        sN[2] = 0.335;
	/**/


/*        // Es una matriz cuadrada, pero se almacenara como un vector a Sx
        VectorDouble Dx(Sx);
        VectorDouble Dx_sN(n, 1);

        Dx_sN = Dx.diagonalProduct(sN);
//Newtlen = Dx_sN.norm2();

        // Dado por el usuario
        maxstep = 1.0;
        // Dado por el usuario
        delta = 0.5;	// dogleg 0.75; //ejemplo

//firstdog = true; // init updTrusReg
	// Init solo doglegdriver
        fc = func.evaluate(xc);
        // Precision Dado por el usuario
        steptol = 10E-6;

        // Mostrar parametros
        cout << "\nHessiana. H:\n" << H;
	waitEnter();
	cout << "\n-----IN-----";
	cout << "\nDimension. n = " << n;
        cout << "\nPunto inicial. xc: " << xc;
        cout << "\nf(xc). Funcion en el punto inicial. fc: " << fc;
	cout << "\ngrad(f(xc)). g = " << g;
        cout << "\nTriangular inferior del Hessiano. L:\n" << L;
        cout << "\nsc^N = -Hc^-1 grad(f(xc)). sN:" << sN;
        cout << "\nEscala metrica. Sx: " << Sx;
	cout << "\nMaxima delta. maxstep = " << maxstep;
	cout << "\nsteptol = " << steptol;
	cout << "\n-----IN/OUT ANTES-----";
	cout << "\ndelta c. Radio de la region de confianza. delta = "<< delta;
	waitEnter();

//cout << "\nPaso de Newton. Newtlen = " << Newtlen;
//cout << "\ntrue: 1a. iteracion. firstdog = " << firstdog;
	cout << "\n------------------------------";
	doubleDogLegDriver(n, xc, fc,
		func, g, L,
                sN, Sx,
                maxstep, steptol,
                delta,
                retcode, xp, fp, maxtaken);

	cout << "\n-----IN/OUT DESPUES-----";
	cout << "\ndelta c. Radio de la region de confianza. delta = "<< delta;
//cout << "\n||s^CP||2. Cauchylen = " << Cauchylen;
//cout << "\nFactor para pasar de CP a ^N. eta = " << eta;
//cout << "\ns^CP, vector de xc a CP. sSD = " << sSD;
//cout << "\neta s^(^N) - s^CP. Vector para pasar de CP a ^N. v = " << v;

	cout << "\n-----OUT-----";
	cout << "\nBandera de retorno. 0: x+ satisf., 1: x+ no satisf. "
        	<< "\nretcode = " << retcode;
	cout << "\nxp = " << xp;
	cout << "\nfp = " << fp;
	cout << "\nmaxtaken = " << maxtaken;
//cout << "\ns^CP + lambda v. Paso. s = " << s;
//cout << "\ntrue: se tomo el paso de Newton. Newttaken = "
//        	<< toString(Newttaken);

  } catch (exception e) {
  	cout << "\nmain: Excepcion " << e.what();
  }
*/

// Fin------------------------------------------------------------------------