// Programa de ajuste polinomial
//Compilar con g++ -I. -I./templates -o polfit polfit.cpp 
//chi2.cpp gnuplot_i_calnum.cpp

#include <iostream>
#include <cmath>
#include <iomanip>
#include <fstream>
#include "algebralineal.h"
#include "templates.h"
#include "chi2.h"
#include "gnuplot_i_calnum.h"
using namespace std;
using namespace CALNUM;
typedef double Real;

int main()
{
//Fichero de datos
// ifstream fin("F.d");
    int n;
    int m;

//Leer numero de parametros = grado del polinomio + 1)
    cin >> m;

//Declaracion de matriz de coeficientes, de covarianzas y vector
// de terminos independientes
    Matrix < Real > mat(m, m), covp(m, m);
    Vector < Real > vec(m);

//Definir vectores de puntos y de errores
    Vector < Real > x;
    Vector < Real > y;
    Vector < Real > sigma;

//Leer puntos
    cin >> x >> y >> sigma;
    n = x.size();

//Verificar que la lectura es correcta
    cout << "Puntos experimentales" << endl << "     x    " <<
          "       y     " << "      sigma  " << endl;
    for (int j = 0; j < n; j++)
        cout << setw(10) << x[j] << "   " << setw(10) << y[j] <<
              "   " << setw(10) << sigma[j] << "   " << endl;
    cout << endl;

//Calcular matriz coeficientes y vector de terminos independientes
    for (int i = 1; i <= m; i++) {
        vec(i) = (pow(x, i - 1) / sigma) * (y / sigma);

        for (int j = 1; j <= i; j++) {
            mat(i, j) = (pow(x, i - 1) / sigma) * (pow(x, j - 1) / sigma);
            if (i > j)
                mat(j, i) = mat(i, j);
        }
    }

//Imprimir matriz de coeficientes
    cout << "Matriz de coeficientes" << endl << mat << endl;
    cout << "vector de terminos independientes" << endl << vec << endl;

//Calculo de las soluciones
    Vector < Real > a(m);
    LU < Real > lu(mat);
    a = lu.solve(vec);
    cout << "Vector de parametros a " << endl << a << endl;

//Calculo de los errores de los parametros
    covp = lu.inv();
    Vector < Real > ea = diag(covp);
    cout << "Vector de errores de los parametros ea " << endl <<
         sqrt(ea) << endl;

//Calculo de chi2
    Real chi2 = normsq((pol(a, x) - y) / sigma);
    Real nu = n - m;
    Real chi2nu = chi2 / nu;
    Real chi2p = Chi2P(chi2, nu);

    cout << "chi2= " << chi2 << "  chi2/nu=  " << chi2nu <<
          " Prob(chi2/nu)= " << chi2p << endl;

//evaluacion del polinomio y funcion en los puntos de dibujo(200)
// se dibuja un 2 * deltax100 % fuera del intervalo
    int ndib = 200;
    double delta = 0.;

//cout << " Entrar delta" <<
//"(Dibujo en [x0-(x1-x0)*delta, x1+(x1-x0)*delta] " << endl << endl;
//cin >> delta;
    double x1 = x[n - 1] + (x[n - 1] - x[0]) * delta;
    double x0 = x[0] - (x1 - x0) * delta;
    double hh = (x1 - x0) / ndib;

    ndib++;
    Vector < double >xx(ndib), yy(ndib), ff(ndib);

    for (int i = 0; i < ndib; i++) {
        xx[i] = x0 + i * hh;
        yy[i] = pol(a, xx[i]);
    }

//Dibujo del polinomio y la funcion
    Gnuplot g1 = Gnuplot();
    g1.set_style("lines");
    g1.plot_xy(xx, yy, "Polinomio ajustado ");
    g1.set_style("points");
    g1.cmd("replot \"parabola.gpl\" with errorbars");

//Descomentar y seleccionar terminal de gnuplot  y extension del fichero
// para imprimir en fichero grafico
// g1.cmd("set terminal postscript");
//g1.cmd("set output 'polfit.ps' ");
//g1.plot_xy(xx, yy, "Polinomio ajustado ");
//g1.cmd("replot \"pol.gpl\" with errorbars");

//Eleccion del tiempo de permanencia de la grafica en pantalla
    int SLEEP_LGTH = 25;

    sleep(SLEEP_LGTH);
    return 0;
}
