// Programa ajuste lineal mediante dos funciones
// Compilar con g++ -o linfit2 linfit2.cpp

#include <iostream>
#include <cmath>
#include <iomanip>
#include <fstream>
using namespace std;

//Introducir aqui las dos funciones con las que se ajusta
// f1-- > 1 f2-- > x linea recta y = a +  b * x 
inline double f1(double x)
{
    return 1. ;
}


inline double f2(double x)
{
    return x;
}


int
main()
{

//Fichero de datos - cambiar cin --> fin
// ifstream fin("tau.d");
    int n;

//Leer numero de puntos n
    cin >> n;

//numero de funciones de ajuste.
    int m = 2;

//Definir matriz de coeficientes y termino independiente
    double **mat;
    mat = new double *[m];

    for (int i = 0; i < m; i++)
        mat[i] = new double[m + 1];

//Definir vectores de puntos y de errores
    double *y = new double[n];
    double *x = new double[n];
    double *sigma = new double[n];

//Definir matriz de covarianzas
    double **covp;
    covp = new double *[m];

    for (int i = 0; i < m; i++)
        covp[i] = new double[m];

//Leer puntos
    for (int i = 0; i < n; i++)
        cin >> x[i] >> y[i] >> sigma[i];
//Cambiar las variables
    for (int i=0; i<n;i++)
      {
	sigma[i]=sigma[i]/y[i];
	y[i]=log(y[i]);
      }
//Verificar que la lectura es correcta
    cout << "Puntos experimentales" << endl;
    for (int j = 0; j < n; j++)
        cout << x[j] << "   " << y[j] << "   " << sigma[j] << "   " << endl;
    cout << endl;
   
//Calcular matriz coeficientes
    for (int i = 0; i < m; i++)
        for (int j = 0; j < m + 1; j++)
            mat[i][j] = 0;
    for (int i = 0; i < n; i++)
        mat[0][0] = mat[0][0] + f1(x[i]) * f1(x[i]) / (sigma[i] * sigma[i]);
    for (int i = 0; i < n; i++)
        mat[0][1] = mat[0][1] + f1(x[i]) * f2(x[i]) / (sigma[i] * sigma[i]);
    for (int i = 0; i < n; i++)
        mat[1][1] = mat[1][1] + f2(x[i]) * f2(x[i]) / (sigma[i] * sigma[i]);
    for (int i = 0; i < n; i++)
        mat[0][2] = mat[0][2] + f1(x[i]) * y[i] / (sigma[i] * sigma[i]);
    for (int i = 0; i < n; i++)
        mat[1][2] = mat[1][2] + f2(x[i]) * y[i] / (sigma[i] * sigma[i]);
    mat[1][0] = mat[0][1];

//Imprimir matriz de coeficientes
    cout << "Matriz coeficientes" << endl;
    for (int i = 0; i < m; i++) {
        cout << endl;
        for (int j = 0; j < m + 1; j++)
            cout << mat[i][j] << "   ";
    }
    cout << endl << endl;

//Calculo de las soluciones
    double a, b, det;

    det = mat[0][0] * mat[1][1] - mat[0][1] * mat[0][1];
    a = mat[0][2] * mat[1][1] - mat[1][2] * mat[0][1];
    b = mat[0][0] * mat[1][2] - mat[0][1] * mat[0][2];
    a = a / det;
    b = b / det;
    cout << "a= " << a << "  b= " << b << endl << endl;

//Calculo de los errores de los parametros
    covp[0][0] = mat[1][1] / det;
    covp[1][1] = mat[0][0] / det;
    covp[1][0] = covp[0][1] = -mat[0][1] / det;
    double ea = sqrt(covp[0][0]);
    double eb = sqrt(covp[1][1]);

    cout << "ea= " << ea << "  eb= " << eb << "  cov(a,b)=" 
         << covp[0][1] << endl << endl;

//Calculo de chi2
    double chi2 = 0;
    for (int i = 0; i < n; i++)
        chi2 = chi2 + pow((a * f1(x[i]) + b * f2(x[i]) - y[i]) / sigma[i], 2);
    int nu = n - 2;
    double chi2nu = chi2 / nu;
    cout << "chi2= " << chi2 << "  chi2/nu=  " << chi2nu << endl;

//Creacion de fichero gnuplot
    ofstream fout("linfit.gpl");
    for (int i = 0 ; i < n; i++ ) 
      fout << x[i] << "  " << y[i] << "  " << sigma[i] << "  " 
           << a + b * x[i] << endl;
    fout.close();

//Destruccion de la memoria asignada
    for (int i = 0; i < m; i++)
        delete[] mat[i];
    for (int i = 0; i < m; i++)
        delete[] covp[i];
    delete[] mat;
    delete[] covp;
    mat = 0;
    covp = 0;
    return 0;
}
