/*
 * Project: MoleCuilder
 * Description: creates and alters molecular systems
 * Copyright (C)  2010-2012 University of Bonn. All rights reserved.
 * 
 *
 *   This file is part of MoleCuilder.
 *
 *    MoleCuilder is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    MoleCuilder is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with MoleCuilder.  If not, see .
 */
/*
 * MatrixContainer.cpp
 *
 *  Created on: Sep 15, 2011
 *      Author: heber
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
#include "CodePatterns/MemDebug.hpp"
#include 
#include 
#include 
#include "CodePatterns/Log.hpp"
#include "KeySetsContainer.hpp"
#include "Fragmentation/helpers.hpp"
#include "Helpers/defs.hpp"
#include "Helpers/helpers.hpp"
#include "MatrixContainer.hpp"
/** Constructor of MatrixContainer class.
 */
MatrixContainer::MatrixContainer()
{
  Header.resize(1);
  RowCounter.resize(1);
  ColumnCounter.resize(1);
  ColumnCounter[0] = -1;
  MatrixCounter = 0;
};
/** Destructor of MatrixContainer class.
 */
MatrixContainer::~MatrixContainer()
{}
/** Either copies index matrix from another MatrixContainer or initialises with trivial mapping if NULL.
 * This either copies the index matrix or just maps 1 to 1, 2 to 2 and so on for all fragments.
 * \param *Matrix pointer to other MatrixContainer
 * \return true - copy/initialisation sucessful, false - dimension false for copying
 */
bool MatrixContainer::InitialiseIndices(class MatrixContainer *_container)
{
  if (_container == NULL) {
    LOG(3, "INFO: Initialising indices with trivial mapping.");
    Indices.resize(MatrixCounter + 1);
    for(int i=MatrixCounter+1;i--;) {
      Indices[i].resize(RowCounter[i]);
      for(int j=RowCounter[i];j--;)
        Indices[i][j] = j;
    }
  } else {
    std::stringstream output;
    if (MatrixCounter != _container->MatrixCounter)
      return false;
    Indices.resize(MatrixCounter + 1);
    for(int i=MatrixCounter+1;i--;) {
      if (RowCounter[i] != _container->RowCounter[i])
        return false;
      Indices[i].resize(_container->RowCounter[i]);
      for(int j=_container->RowCounter[i];j--;) {
        Indices[i][j] = _container->Indices[i][j];
        output << Indices[i][j] << "\t";
      }
    }
    LOG(3, "INFO: Initialising indices from other MatrixContainer: " << output.str());
  }
  return true;
};
/** Parsing a number of matrices.
 *    -# open the matrix file
 *    -# skip some lines (\a skiplines)
 *    -# scan header lines for number of columns
 *    -# scan lines for number of rows
 *    -# allocate a temporary matrix
 *    -# loop over found column and row counts and parse in each entry
 *    -# use MatrixContainer::AddMatrix() to add the parsed matrix to the internal
 * \param &input input stream
 * \param skiplines number of inital lines to skip
 * \param skiplines number of inital columns to skip
 * \param MatrixNr index number in Matrix array to parse into
 * \return parsing successful
 */
bool MatrixContainer::ParseMatrix(std::istream &input, int skiplines, int skipcolumns, size_t MatrixNr)
{
  stringstream line;
  string token;
  char filename[1023];
  if (input.fail()) {
    ELOG(1, endl << "MatrixContainer::ParseMatrix: Unable to parse istream.");
    //performCriticalExit();
    return false;
  }
  // parse header
  std::string header;
  char dummy[1024];
  for (int m=skiplines+1;m--;)
    input.getline(dummy, 1023);
  line.str(dummy);
  for(int k=skipcolumns;k--;)
    line >> header;
  LOG(3, "INFO: Header of Matrix " << MatrixNr << " :" << line.str());
  // scan header for number of columns
  size_t ColumnCounter = 0;
  while ( getline(line,token, '\t') ) {
    if (token.length() > 0)
      ColumnCounter++;
  }
  LOG(3, "INFO: "+line.str());
  // scan rest for number of rows/lines
  size_t RowCounter = -1;
  while (!input.eof()) {
    input.getline(filename, 1023);
    LOG(3, "INFO: Comparing: " << strncmp(filename,"MeanForce",9));
    RowCounter++; // then line was not last MeanForce
    if (strncmp(filename,"MeanForce", 9) == 0) {
      break;
    }
  }
  // allocate temporary matrix
  MatrixArray temp_matrix;
  temp_matrix.resize(RowCounter);
  for(MatrixArray::iterator iter = temp_matrix.begin(); iter != temp_matrix.end(); ++iter)
    (*iter).resize(ColumnCounter);
  // parse in each entry for this matrix
  input.clear();
  input.seekg(ios::beg);
  for (int m=skiplines+1;m--;)
    input.getline(dummy, 1023);    // skip header
  line.str(dummy);
  LOG(3, "INFO: Header: " << line.str());
  for(int k=skipcolumns;k--;)  // skip columns in header too
    line >> filename;
  header = line.str();
  for(size_t j=0;j> filename;
    for(size_t k=0;(k> temp_matrix[j][k];
      output << " " << std::setprecision(2) << temp_matrix[j][k] << endl;
    }
    LOG(3, output.str());
  }
  // finally add the matrix
  return AddMatrix(header, temp_matrix, MatrixNr);
}
/** Adds a matrix at position \a MatrixNr to MatrixContainer::Matrix.
 *
 * @param header header to add for this matrix
 * @param matrix to add
 * @param MatrixNr position in MatrixContainer::Matrix.
 * @return true - insertion ok, false - invalid matrix
 */
bool MatrixContainer::AddMatrix(const std::string &header, const MatrixArray &matrix, size_t MatrixNr)
{
  // make some pre-checks
  if (header.size() == 0)
    ELOG(2, "The given header of the matrix to add is empty.");
  if (matrix.size() == 0) {
    ELOG(1, "RowCounter[" << MatrixNr << "]: " << RowCounter[MatrixNr] << " from input stream.");
    return false;
  }
  if (matrix[0].size() == 0) {
    ELOG(1, "ColumnCounter[" << MatrixNr << "]: " << ColumnCounter[MatrixNr] << " from ostream.");
    return false;
  }
  // add header
  if (Header.size() <= MatrixNr)
    Header.resize(MatrixNr+1);
  Header[MatrixNr] = header;
  // row count
  if (RowCounter.size() <= MatrixNr)
    RowCounter.resize(MatrixNr+1);
  RowCounter[MatrixNr] = matrix.size();
  LOG(4, "INFO: RowCounter[" << MatrixNr << "]: " << RowCounter[MatrixNr] << " from input stream.");
  // column count
  if (ColumnCounter.size() <= MatrixNr)
    ColumnCounter.resize(MatrixNr+1);
  ColumnCounter[MatrixNr] = matrix[0].size();
  LOG(4, "INFO: ColumnCounter[" << MatrixNr << "]: " << ColumnCounter[MatrixNr] << ".");
  // allocate matrix ...
  if (Matrix.size() <= MatrixNr)
    Matrix.resize(MatrixNr+1);
  MatrixCounter = Matrix.size()-1;
  Matrix[MatrixNr].resize(RowCounter[MatrixNr] + 1);
  for(int j=0;j<=RowCounter[MatrixNr];++j)
    Matrix[MatrixNr][j].resize(ColumnCounter[MatrixNr]+1);
  // .. and copy values
  for(int j=0;j max)
          max = fabs(Matrix[i][j][k]);
  if (fabs(max) < MYEPSILON)
    max += MYEPSILON;
 return max;
};
/** Scans all elements of MatrixContainer::Matrix for smallest absolute value.
 * \return smallest value of MatrixContainer::Matrix
 */
double MatrixContainer::FindMinValue()
{
  double min = Matrix[0][0][0];
  for(int i=MatrixCounter+1;i--;)
    for(int j=RowCounter[i]+1;j--;)
      for(int k=ColumnCounter[i];k--;)
        if (fabs(Matrix[i][j][k]) < min)
          min = fabs(Matrix[i][j][k]);
  if (fabs(min) < MYEPSILON)
    min += MYEPSILON;
  return min;
};
/** Sets all values in the last of MatrixContainer::Matrix to \a value.
 * \param value reset value
 * \param skipcolumns skip initial columns
 * \return true if successful
 */
bool MatrixContainer::SetLastMatrix(double value, int skipcolumns)
{
  for(int j=RowCounter[MatrixCounter]+1;j--;)
    for(int k=skipcolumns;k RowCounter[ KeySets.OrderSet[Order][CurrentFragment] ]) {
                ELOG(0, "In fragment No. " << KeySets.OrderSet[Order][CurrentFragment]   << " current force index " << m << " is greater than " << RowCounter[ KeySets.OrderSet[Order][CurrentFragment] ] << "!");
                performCriticalExit();
                return false;
              }
              if (Order == SubOrder) { // equal order is always copy from Energies
                for(int l=ColumnCounter[ KeySets.OrderSet[SubOrder][j] ];l--;) // then adds/subtract each column
                  Matrix[ KeySets.OrderSet[Order][CurrentFragment] ][m][l] += MatrixValues.Matrix[ KeySets.OrderSet[SubOrder][j] ][k][l];
              } else {
                for(int l=ColumnCounter[ KeySets.OrderSet[SubOrder][j] ];l--;)
                  Matrix[ KeySets.OrderSet[Order][CurrentFragment] ][m][l] -= Matrix[ KeySets.OrderSet[SubOrder][j] ][k][l];
              }
            }
            //if ((ColumnCounter[ KeySets.OrderSet[SubOrder][j] ]>1) && (RowCounter[0]-1 >= 1))
             //LOG(0, "Fragments[ KeySets.OrderSet[" << Order << "][" << CurrentFragment << "]=" << KeySets.OrderSet[Order][CurrentFragment] << " ][" << RowCounter[0]-1 << "][" << 1 << "] = " <<  Matrix[ KeySets.OrderSet[Order][CurrentFragment] ][RowCounter[0]-1][1]);
          }
        } else {
          //LOG(0, "Fragment " << KeySets.OrderSet[SubOrder][j] << " is not contained in fragment " << KeySets.OrderSet[Order][CurrentFragment] << ".");
        }
      }
    }
   //LOG(0, "Final Fragments[ KeySets.OrderSet[" << Order << "][" << CurrentFragment << "]=" << KeySets.OrderSet[Order][CurrentFragment] << " ][" << KeySets.AtomCounter[0]-1 << "][" << 1 << "] = " <<  Matrix[ KeySets.OrderSet[Order][CurrentFragment] ][KeySets.AtomCounter[0]-1][1]);
  }
  return true;
};
/** Writes the summed total fragment terms \f$F_{ij}\f$ to file.
 * \param *name inputdir
 * \param *prefix prefix before \a EnergySuffix
 * \return file was written
 */
bool MatrixContainer::WriteTotalFragments(const std::string name, const std::string prefix)
{
  ofstream output;
  char *FragmentNumber = NULL;
  LOG(1, "STATUS: Writing fragment files.");
  for(int i=0;i