// $Id: ModelDataSet.h 1365 2024-06-28 20:00:02Z ge $
/// \file ModelDataSet.h
/// \brief contains the ModelDatSet class
///
/// $Revision: 1365 $
/// \author Gerald Weber <gweberbh@gmail.com>
#ifndef GBC_MODELDATASET_H
#define GBC_MODELDATASET_H "$Id: ModelDataSet.h 1365 2024-06-28 20:00:02Z ge $"
#include <limits>
#include "SequenceDataset.h"
#include "Range.h"

namespace gbc
{
template<class _Model>
// ModelDataSet combines a model like IndividualNearestNeighbours with a data set
class ModelDataSet
  {
  public:
    typedef _Model                                 model_type;
    typedef typename model_type::value_type        value_type;
    typedef std::valarray<value_type>              vector_type;
    typedef typename model_type::sequenceinfo_type sequenceinfo_type;
    typedef typename model_type::matrix_type       matrix_type;
    typedef typename model_type::svd_deque_type    svd_deque_type;
    typedef SequenceDataset<sequenceinfo_type>     sequence_dataset_type;
    typedef std::deque<std::string>                string_deque_type;

    sequence_dataset_type dataset;
    model_type            *model;
    std::string           data_set_type;
    svd_deque_type        SVDVariables;

    ModelDataSet(void): model() {}

   inline void verify(std::ostream& out)
      {
      dataset.calculate_and_print_verify(out);
      }

   inline void print_tex(std::ostream& out)
     {
     dataset.print_tex(out);
     }

    /// \brief Calculates the difference between the predicted and the adjusted temperature
    inline void predict_all(void)
      {
      for(auto &si : dataset.Raw_dataset)
        {
        model->Sequence_info=&si;
        model->reset_calculation();
        model->melting_temperature();
        }
      }
      
    inline value_type predict_all_adjusting(Range<value_type> &en, std::deque<std::string> &exclude, std::string &adjust_par)
      {
      bool fixed_entropy = adjust_par == "DeltaS";
      
      std::set<std::string> nn_exc;
      for(auto &ex : exclude) nn_exc.insert(ex);
      
      std::set<std::string> FullSet;
        
      dataset.calculate_av_tm_nn_type();
      value_type best_sqr_diff = std::numeric_limits<value_type>::max();
      value_type best_en = en.begin();
      for(en.begin(); !en.finished(); ++en)
        {
        for(auto &si : dataset.Raw_dataset)
          {
          model->Sequence_info=&si;
          model->set_TM_parameters();
          model->reset_calculation();
          std::set<std::string> Set=model->get_nn();
          for(auto NN : Set)
            {
            FullSet.insert(NN);
            if (nn_exc.find(NN) == nn_exc.end())
              {
              value_type Tm = dataset.StatNNTemperature[NN].first + 273.15;
              if (fixed_entropy)
                {
                model->Enthalpies[NN] = (model->Enthalpies[NN]*1000 + Tm * ( en.current() - model->Entropies[NN] ) )/1000.0;
                model->Entropies[NN] = en.current();
                }
              else
                {
                model->Entropies[NN] = model->Entropies[NN] + (1/Tm) * ( en.current() - model->Enthalpies[NN] )*1000.0;
                model->Enthalpies[NN] = en.current();
                }
              }
            }
          model->melting_temperature();
          }
        dataset.calculate_tm_difference_entire_set();
        value_type sqr_diff = dataset.Temperature.sqr_diff;
        CERR_DEBUG(DFEM_SHCHI2) << "En=" << en.current() << " chi2=" << sqr_diff << std::endl;

        if (best_sqr_diff > sqr_diff) 
          {
          best_sqr_diff = sqr_diff;
          best_en = en.current();
          }
        }
        
      for(auto NN : FullSet)
        {
        if (nn_exc.find(NN) == nn_exc.end())
          {
          std::string enthalpy_name=NN + ":" + "enthalpy";
          value_type enth = model->parameter_map.get_parameter_or_generic(enthalpy_name);
      
          std::string entropy_name=NN + ":" + "entropy";
          value_type entr = model->parameter_map.get_parameter_or_generic(entropy_name);
      
          value_type Tm = dataset.StatNNTemperature[NN].first + 273.15;
      
          if (fixed_entropy)
            {
            enth = (enth*1000 + Tm * ( best_en - entr ))/1000.0;
            model->parameter_map.set_parameters(enthalpy_name,enth);
            model->parameter_map.set_parameters(entropy_name,best_en);
            }
          else
            {
            entr = entr + (1/Tm) * ( best_en - enth )*1000.0;
            model->parameter_map.set_parameters(enthalpy_name,best_en);
            model->parameter_map.set_parameters(entropy_name,entr);
            }
          }
        }

        for(auto &si : dataset.Raw_dataset)
          {
          model->set_TM_parameters();
          model->Sequence_info=&si;
          model->reset_calculation();
          model->melting_temperature();
          }
      dataset.calculate_tm_difference_entire_set();

      return best_en;
      }

      
    inline void predict_and_print_sequentially(std::ostream& out)
      {
      bool first_line=true;
      std::ifstream xml;
      double multspconc=1;
      typename sequence_dataset_type::species_concentration_deque_type spconc;
      size_t number_of_sequences=0;
      for (auto xmlfile : dataset.Data_file_deque)
        {
        dataset.open_and_read_xml_header(xml,xmlfile);
        while(!xml.eof()) 
          {
          dataset.read_xml_line(xml,xmlfile,spconc,multspconc);
          number_of_sequences++;
          for(auto &si : dataset.Raw_dataset) 
            {
            predict_all();
          
            dataset.collect_tm_difference(si);
          
            if (first_line) si.print_head(out);
            first_line=false;
            si.print(out);
            }
          dataset.Raw_dataset.clear();
          }
        xml.close();
        }
      dataset.calculate_tm_statistics();
      }
      
      
    //Rules are read from parameter files, make sure that this is set before read_xml
    //otherwise the rules are not applied to the dataset
    void read_rules_and_parameters_from_list_of_files(string_deque_type &par_file_deque)
      {
      dataset.read_rules_from_list_of_files(par_file_deque);
      model->get_parameters_from_list_of_files(par_file_deque);
      }

    
    inline void set_svd_variables(const svd_deque_type &svd)
      {
      SVDVariables=svd;
      }
      
    inline void set_svd_matrices(matrix_type &A, matrix_type &B)
      {
      matrix_type M;
      int i=0;
      for(auto &si : dataset.Raw_dataset)
        {
        model->Sequence_info=&si;
        value_type b;
        model->get_svd_line(SVDVariables,M,b);
        if (A.Rows ==0 or A.Columns == 0) A.resize(dataset.Raw_dataset.size(),M.Columns);
        for (int j=0; j < M.Columns; j++) A(i,j)=M(0,j);
        if (B.Rows ==0 or B.Columns == 0) B.resize(dataset.Raw_dataset.size(),1);
        B(i,0)=b;
        i++;
        }
      CERR_DEBUG(DFEM_SHSVDMAT) << "A(" << A.Rows << "," << A.Columns << ")" << std::endl;
      CERR_DEBUG(DFEM_SHSVDMAT) << A << std::endl;
      CERR_DEBUG(DFEM_SHSVDMAT) << "B(" << B.Rows << "," << B.Columns << ")" << std::endl;
      CERR_DEBUG(DFEM_SHSVDMAT) << B << std::endl;
      }
  
    inline void adjust_svd_units(vector_type &x)
      {
      int i=0;
      for(auto &v: SVDVariables)
        {
        if (reg_match(v,":enthalpy$")) x[i] /= 1000;//cal/mol => kcal/mol
        i++;
        }
      }

  };

}
#endif
