/* 
 * File:   main.c
 * Author: Andréa
 *
 * Created on 20 octobre 2010, 23:13
 */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include "constants.h"
#include "logger.h"
#include "method.h"

#define LOG_LEVEL INFO
#define displayResult(a,b) logMessage(INFO,"result","=======================");     \
                           logMessage(INFO,"resolution method",b->_name);      \
                           logMessage(INFO,"optimization result : complexity",integerToString(b->_last_complexity,10)); \
                           logMessage(INFO,"optimization result : input",printVector(b->_last_result,pb->_dimension)); \
                           logMessage(INFO,"optimization result : function value",doubleToString(b->_last_result_function_value)); \
                           if(a->_solution) logMessage(INFO,"optimization result : absolute error",doubleToString(b->_last_absolute_error)); \
                           if(a->_solution) logMessage(INFO,"optimization result : relative error",doubleToString(b->_last_relative_error));

double  test_vector[] = {5,-8};
double solution[] = {0,0};
size_t dimension  = 2;

extern
void resolveByGradientFixedStep(
    Problema*    pb,
    Method*      m,
    double       init_vector[],
    void*        args[]);

extern
void resolveByGradientOptimalStep(
    Problema*    pb,
    Method*      m,
    double       init_vector[],
    void*        args[]);

extern
void resolveByNearlyNewton(
    Problema*    pb,
    Method*      m,
    double       init_vector[],
    void*        args[]);

extern
double* ES(const double x[], const size_t dimension);

extern
double* gradientES(const double x[]);

extern
double* F(const double x[], const size_t dimension);

extern
double* gradientF(const double x[]);

extern
double* G(const double x[], const size_t dimension);

extern
double* gradientG(const double x[]);

extern
double* rosenbrock(const double x[], const size_t dimension);

extern
double* gradientRosenbrockTested(const double x[]);

boolean launchBenchmark(
    char      functionRepresentation[MAX_INPUT],
    double* (*function)  (const double[],const size_t dimension),
    double* (*derivee1)  (const double[]),
    size_t    functionDimension,
    double*   initial_vector,
    double*   solution
)
{
    Problema* pb     = NULL;
    Method  * method = NULL;
    //double  test_vector[] = {-0.5, 0.5};
    //double solution[] = {1.0, 1.0};
    double step = 0.001;
    void* parameters[] = {
        (void *)&step
    };

    pb = createProblemaWithParameters(
            "Problema TP Noté : rosenbrock",
            1,
            functionRepresentation,
            function,
            functionDimension
            );

    // solution known
    if(solution)
        pb->_solution = solution;

    // FIXME
    pb->_derivees[0] = derivee1;

    logMessage(INFO,"problem",pb->display(pb));

    method = createMethodWithParameters(
            "Gradient à pas fixe",
            1,
            resolveByGradientFixedStep
            );

    setLogLevelDisplayed(LOG_LEVEL);

    logMessage(INFO,"vector test",printVector(initial_vector,pb->_dimension));

    method->_method_function(
        pb,
        method,
        initial_vector,
        parameters
    );

    if(!method->_last_result)
    {
        logMessage(SEVERE,"optimization","result not stored");
        return 0;
    }

    displayResult(pb,method);
    
    destroyMethod(&method);

    method = createMethodWithParameters(
            "Gradient à pas optimal",
            2,
            resolveByGradientOptimalStep
            );

    method->_method_function(
        pb,
        method,
        initial_vector,
        parameters
    );

    if(!method->_last_result)
    {
        logMessage(SEVERE,"optimization","result not stored");
        return 0;
    }

    displayResult(pb,method);

    destroyMethod(&method);

    method = createMethodWithParameters(
            "Quasi-Newton",
            3,
            resolveByNearlyNewton
            );

    method->_method_function(
        pb,
        method,
        initial_vector,
        parameters
    );

    if(!method->_last_result)
    {
        logMessage(SEVERE,"optimization","result not stored");
        return 0;
    }

    displayResult(pb,method);

}

boolean resolveTPFunction(
    char      functionRepresentation[MAX_INPUT],
    double* (*function)  (const double[],const size_t dimension),
    double* (*derivee1)  (const double[]),
    size_t    functionDimension,
    double*   initial_vector,
    double*   solution
)
{

    Problema* pb     = NULL;
    Method  * method = NULL;
    //double  test_vector[] = {-0.5, 0.5};
    //double solution[] = {1.0, 1.0};
    double step = 0.001;
    void* parameters[] = {
        (void *)&step
    };

    pb = createProblemaWithParameters(
            "Problema TP Noté : rosenbrock",
            1,
            functionRepresentation,
            function,
            functionDimension
            );

    // solution known
    if(solution)
        pb->_solution = NULL;

    // FIXME
    pb->_derivees[0] = derivee1;

    logMessage(INFO,"problem",pb->display(pb));
    logMessage(INFO,"vector initial",printVector(initial_vector,pb->_dimension));

    method = createMethodWithParameters(
            "Gradient à pas fixe",
            1,
            resolveByGradientFixedStep
            );

    method->_epsilon_in = 0.00001;
    method->_max_iteration = 10000;

    step = 0.25;

    method->_method_function(
        pb,
        method,
        initial_vector,
        parameters
    );

    displayResult(pb,method);

    step = 0.125;

    method->_method_function(
        pb,
        method,
        initial_vector,
        parameters
    );

    displayResult(pb,method);

    step = 0.05;

    method->_method_function(
        pb,
        method,
        initial_vector,
        parameters
    );

    displayResult(pb,method);

    step = 0.025;

    method->_method_function(
        pb,
        method,
        initial_vector,
        parameters
    );

    displayResult(pb,method);

    method = createMethodWithParameters(
            "Gradient à pas optimal",
            2,
            resolveByGradientOptimalStep
            );

    method->_epsilon_in = 0.00001;

    method->_method_function(
        pb,
        method,
        initial_vector,
        parameters
    );

    displayResult(pb,method);
}

boolean resolveTPOptimalFunction(
    char      functionRepresentation[MAX_INPUT],
    double* (*function)  (const double[],const size_t dimension),
    double* (*derivee1)  (const double[]),
    size_t    functionDimension,
    double*   initial_vector,
    double*   solution
)
{

    Problema* pb     = NULL;
    Method  * method = NULL;
    //double  test_vector[] = {-0.5, 0.5};
    //double solution[] = {1.0, 1.0};
    double step = 0.001;
    void* parameters[] = {
        (void *)&step
    };

    pb = createProblemaWithParameters(
            "Problema TP Noté : G",
            1,
            functionRepresentation,
            function,
            functionDimension
            );

    // solution known
    if(solution)
        pb->_solution = NULL;

    // FIXME
    pb->_derivees[0] = derivee1;

    logMessage(INFO,"problem",pb->display(pb));
    logMessage(INFO,"vector initial",printVector(initial_vector,pb->_dimension));

    method = createMethodWithParameters(
            "Gradient à pas fixe",
            1,
            resolveByGradientOptimalStep
            );

    method->_method_function(
        pb,
        method,
        initial_vector,
        parameters
    );

    displayResult(pb,method);
}

int main(int argc, char** argv) {
    
    // Init the random seed
    srand (time (NULL));

    setLogLevelDisplayed(LOG_LEVEL);

    if(!resolveTPOptimalFunction(
            "F(x) = x_{1}^{2} + x_{2}^{2}",
            G,
            gradientG,
            dimension,
            &test_vector[0],
            solution
      ))
    {
        logMessage(SEVERE,"resolution error","unexpected error");
        return -1;
    }

    printf("\n Test result : 'N/A'\n");

    return (EXIT_SUCCESS);
}

