/// $Revision: 1335 $
/// \author Gerald Weber <gweberbh@gmail.com>
#ifndef GBC_LINEARMINIMIZATION_H
#define GBC_LINEARMINIMIZATION_H "$Id: LinearMinimization.h 1335 2022-12-24 14:15:55Z ge $"
#include <fstream>
#include <iterator>
#include <limits>
#include "MathAux.h"
#include <gsl/gsl_linalg.h>
#include <sys/time.h> // for clock_gettime()
#include "valmatrix.h"
#include "ErrorCodes.h"


#undef max

namespace gbc
  {
  class LinearMinimization
    {
    public:
      typedef double value_type;
      typedef valmatrix<value_type> matrix_type;
      typedef std::valarray<value_type> vector_type;
      
      vector_type x;
      gsl_matrix *gA, *U, *V;
      gsl_vector *S, *gB;
      value_type ZeroLimit;
      
      
   LinearMinimization(void): x(), gA(), U(), V(), S(), gB(), ZeroLimit()
     {
     }

   ~LinearMinimization(void)
     {
     if (gA != NULL) gsl_matrix_free(gA);
     if (U  != NULL) gsl_matrix_free(U);
     if (V  != NULL) gsl_matrix_free(V);
     if (S  != NULL) gsl_vector_free(S);
     if (gB != NULL) gsl_vector_free(gB);  
     }

   void set(const matrix_type &a, const matrix_type &b)
     {
     gA = gsl_matrix_alloc(a.Rows,a.Columns);
     for(size_t i=0; i < a.Rows; ++i)
        for(size_t j=0; j < a.Columns; ++j)
          gsl_matrix_set(gA,i,j,a(i,j));
        
     gB = gsl_vector_alloc(b.Rows);
     for(size_t i=0; i < b.Rows; ++i) gsl_vector_set(gB,i,b(i,0));
     }

   inline void run(void)
     {
     //Time accounting
     struct timeval start, current;
     gettimeofday(&start, NULL);
     long int seconds_elapsed;
     
     const size_t M = gA->size1, N=gA->size2;
          
     U = gsl_matrix_alloc(M,N);
     gsl_matrix_memcpy(U, gA);
     V = gsl_matrix_alloc(N,N);
     S = gsl_vector_alloc (N);
     gsl_vector *work = gsl_vector_alloc (N);
     
     //https://www.gnu.org/software/gsl/doc/html/linalg.html#c.gsl_linalg_SV_decomp
     int status=gsl_linalg_SV_decomp(U,V,S,work);
     if (status != GSL_SUCCESS)
       {
       CERR_ERROR(ERRSVDECOMP) << "gsl_strerror=" << gsl_strerror (status) << std::endl;
       CERR_TERM
       }
     CERR_DEBUG(DLMI_PRNTS) << "S(" << S->size << "," << S->size << ")" << std::endl;
     int rank=0;
     for (int i=0; i < S->size; i++)
       {
       value_type s=gsl_vector_get(S,i);
       if (s > ZeroLimit) rank++;
       CERR_DEBUG(DLMI_PRNTS) << s << std::endl;
       }
       
     COUT_INFO(INFOSVDSRI) << "SVD sucessful, S rank is " << rank << " (larger than " << ZeroLimit << ")" << std::endl;

     gsl_vector_free(work);

     
     gsl_vector *gx = gsl_vector_alloc(N);
     //https://www.gnu.org/software/gsl/doc/html/linalg.html#c.gsl_linalg_SV_solve
     status = gsl_linalg_SV_solve(U,V,S,gB,gx);
     if (status != GSL_SUCCESS)
       {
       CERR_ERROR(ERRSVSOLVE) << "gsl_strerror=" << gsl_strerror (status) << std::endl;
       CERR_TERM
       }
   
     x.resize(gx->size);
     for (int i=0; i < gx->size; i++) x[i]=gsl_vector_get(gx,i);
     gsl_vector_free(gx);
     
//      matrix_type Chi=A*x-B;
     }
  };
}
#endif
