// $Id: Regression.h 1282 2022-02-15 09:23:56Z ge $
// Copyright 2006 Gerald Weber
/// \file Regression.h
/// \brief contains the Regression function class
///
/// $Revision: 1282 $
/// \author Gerald Weber <gweberbh@gmail.com>
#ifndef GBC_REGRESSION_H
#define GBC_REGRESSION_H "$Id: Regression.h 1282 2022-02-15 09:23:56Z ge $"
#include <gsl/gsl_fit.h>
#include <valarray>
#include "MathAux.h"
#include "ErrorCodes.h"

namespace gbc
  {
  template<class _Tp=double>
  
  /// \brief Class which calculates linear regression
  ///
  /// \attention This class needs the GNU Scientific Library (GSL) 
  class Regression
    {
    public:
    typedef _Tp value_type;
    typedef std::pair<value_type,value_type> coefficient_type;

    value_type *x_data, *y_data;
    size_t x_data_size, y_data_size;
    value_type expoent;
    double c0, c1, cov00, cov01, cov11, chisq;
    bool Debug;

    Regression(value_type e=1.0): x_data_size(1), y_data_size(1), expoent(e), Debug(false)
      {
      x_data=new value_type[1];
      y_data=new value_type[1];
      }

    ~Regression(void)
      {
      delete[] x_data;
      delete[] y_data;
      }
      
    inline void print_regression_info(std::ostream &out)
      {
      out << "x_data_size y_data_size expoent c0 c1 cov00 cov01 cov11 chisq" << std::endl;
      out << x_data_size << " " << y_data_size << " " << expoent << " " << c0 << " " << c1 << " "
          << cov00 << " " << cov01 << " " << cov11 << " " << chisq << std::endl;
      }
      
    inline void print_vectors(std::ostream &out)
      {
      for (size_t n=0; n < x_data_size; n++)
        {
        out << x_data[n] << " " << y_data[n] << std::endl;
        }
      }
      

    template<class _VectorTp>
    inline void x(const _VectorTp& xv)
      {
      if (x_data_size != xv.size()) 
        {
        x_data_size=xv.size();
        delete[] x_data;
        x_data=new value_type[xv.size()];
        }
      size_t n;
      for(n=0; n < xv.size(); ++n) x_data[n]=pow(xv[n],expoent);
      }

    template<class _VectorTp>
    inline void y(const _VectorTp& yv)
      {
      if (y_data_size != yv.size()) 
        {
        y_data_size=yv.size();
        delete[] y_data;
        y_data=new value_type[yv.size()];
        }
      size_t n;
      for(n=0; n < yv.size(); ++n) y_data[n]=yv[n];
      }

    template<class _Tp2, class _Tp3>
    inline void y(const std::map<_Tp2,_Tp3>& yv)
      {
      std::valarray<_Tp3> yvec(yv.size());
      convert(yv,yvec);
      y(yvec);
      }


    template<class _MapTp>
    /// \brief Assigns a map to the x and y vectors;
    inline void xy(const _MapTp& xy_map)
      {
      std::valarray<value_type> xv(xy_map.size()), yv (xy_map.size());
      convert(xy_map,xv,yv);
      x(xv); y(yv);
      }

    coefficient_type calculate_regression(void)
      {
      gsl_fit_linear (x_data, 1, y_data, 1, x_data_size, &c0, &c1, &cov00, &cov01, &cov11, &chisq);
      
      //Harmless error, its not a problem when this happens
      if (BOOL_DDEBUG(DREG_FSRCRNAN) and (std::isnan(c0) || std::isnan(c1)))
        {
        CERR_DEBUG(DREG_FSRCRNAN) << " regression failed, some regression coefficients returned NaN" << std::endl;
        print_regression_info(CERR);
        CERR << "x vs. y" << std::endl;
        print_vectors(CERR);
        }
        
      return coefficient_type(c0,c1);
      }

    template<class _VectorTp>
    coefficient_type calculate_regression(const _VectorTp& yv)
      {
      y(yv);
      return calculate_regression();
      }

    template<class _VectorTp>
    coefficient_type calculate_regression(const _VectorTp& xv, const _VectorTp& yv)
      {
      x(xv); y(yv);
      return calculate_regression();
      }

  };
}
#endif
