/*
 * 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 .
 */
/** \file analyzer.cpp
 *
 * Takes evaluated fragments (energy and forces) and does evaluation of how sensible the BOSSANOVA
 * approach was, e.g. in the decay of the many-body-contributions.
 *
 */
//============================ INCLUDES ===========================
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
#include "CodePatterns/MemDebug.hpp"
#include 
#include 
#include 
#include 
#include "datacreator.hpp"
#include "Element/periodentafel.hpp"
#include "Fragmentation/defs.hpp"
#include "Fragmentation/EnergyMatrix.hpp"
#include "Fragmentation/ForceMatrix.hpp"
#include "Fragmentation/helpers.hpp"
#include "Fragmentation/HessianMatrix.hpp"
#include "Fragmentation/KeySetsContainer.hpp"
#include "CodePatterns/Log.hpp"
#include "CodePatterns/Verbose.hpp"
//============================== MAIN =============================
int main(int argc, char **argv)
{
  periodentafel *periode = NULL; // and a period table of all elements
  EnergyMatrix Energy;
  EnergyMatrix EnergyFragments;
  ForceMatrix Force;
  ForceMatrix ForceFragments;
  HessianMatrix Hessian;
  HessianMatrix HessianFragments;
  EnergyMatrix Hcorrection;
  EnergyMatrix HcorrectionFragments;
  ForceMatrix Shielding;
  ForceMatrix ShieldingPAS;
  ForceMatrix Chi;
  ForceMatrix ChiPAS;
  EnergyMatrix Time;
  ForceMatrix ShieldingFragments;
  ForceMatrix ShieldingPASFragments;
  ForceMatrix ChiFragments;
  ForceMatrix ChiPASFragments;
  KeySetsContainer KeySet;
  std::ofstream output;
  std::ofstream output2;
  std::ofstream output3;
  std::ofstream output4;
  std::ifstream input;
  std::stringstream filename;
  time_t t = time(NULL);
  struct tm *ts = localtime(&t);
  char *datum = asctime(ts);
  std::stringstream Orderxrange;
  std::stringstream Fragmentxrange;
  std::stringstream yrange;
  char *dir = NULL;
  bool NoHessian = false;
  bool NoTime = false;
  bool NoHCorrection = true;
  int counter = 0;
  LOG(0, "ANOVA Analyzer");
  LOG(0, "==============");
  // Get the command line options
  if (argc < 4) {
    LOG(0, "Usage: " << argv[0] << "    [elementsdb]");
    LOG(0, "\ttherein the output of a molecuilder fragmentation is expected, each fragment with a subdir containing an energy.all and a forces.all file.");
    LOG(0, "\tprefix of energy and forces file.");
    LOG(0, "\tcreated plotfiles and datafiles are placed into this directory ");
    LOG(0, "[elementsdb]\tpath to elements database, needed for shieldings.");
    return 1;
  } else {
    dir = new char[strlen(argv[2]) + 2];
    strcpy(dir, "/");
    strcat(dir, argv[2]);
  }
  if (argc > 4) {
    LOG(0, "Loading periodentafel.");
    periode = new periodentafel;
    periode->LoadPeriodentafel(argv[4]);
  }
  // Test the given directory
  if (!TestParams(argc, argv)) {
    delete dir;
    delete periode;
    return 1;
  }
  // +++++++++++++++++ PARSING +++++++++++++++++++++++++++++++
  // ------------- Parse through all Fragment subdirs --------
  if (!Energy.ParseFragmentMatrix(argv[1], dir, EnergySuffix,0,0)) return 1;
  if (!Hcorrection.ParseFragmentMatrix(argv[1], "", HCORRECTIONSUFFIX,0,0)) {
    NoHCorrection = true;
    ELOG(2, "No HCorrection file found, skipping these.");
  }
  
  if (!Force.ParseFragmentMatrix(argv[1], dir, ForcesSuffix,0,0)) return 1;
  if (!Hessian.ParseFragmentMatrix(argv[1], dir, HessianSuffix,0,0)) {
    NoHessian = true;
    ELOG(2, "No Hessian file found, skipping these.");
  }
  if (!Time.ParseFragmentMatrix(argv[1], dir, TimeSuffix, 10,1)) {
    NoTime = true;
    ELOG(2, "No speed file found, skipping these.");
  }
  if (periode != NULL) { // also look for PAS values
    if (!Shielding.ParseFragmentMatrix(argv[1], dir, ShieldingSuffix, 1, 0)) return 1;
    if (!ShieldingPAS.ParseFragmentMatrix(argv[1], dir, ShieldingPASSuffix, 1, 0)) return 1;
    if (!Chi.ParseFragmentMatrix(argv[1], dir, ChiSuffix, 1, 0)) return 1;
    if (!ChiPAS.ParseFragmentMatrix(argv[1], dir, ChiPASSuffix, 1, 0)) return 1;
  }
  // ---------- Parse the TE Factors into an array -----------------
  if (!Energy.ParseIndices()) return 1;
  if (!NoHCorrection) Hcorrection.ParseIndices();
  // ---------- Parse the Force indices into an array ---------------
  if (!Force.ParseIndices(argv[1])) return 1;
  if (!ForceFragments.AllocateMatrix(Force.Header, Force.MatrixCounter, Force.RowCounter, Force.ColumnCounter)) return 1;
  if (!ForceFragments.InitialiseIndices((class MatrixContainer *)&Force)) return 1;
  // ---------- Parse hessian indices into an array -----------------
  if (!NoHessian) {
    if (!Hessian.InitialiseIndices((class MatrixContainer *)&Force)) return 1;
    if (!HessianFragments.AllocateMatrix(Hessian.Header, Hessian.MatrixCounter, Hessian.RowCounter, Hessian.ColumnCounter)) return 1;
    if (!HessianFragments.InitialiseIndices((class MatrixContainer *)&Force)) return 1;
  }
  // ---------- Parse the shielding indices into an array ---------------
  if (periode != NULL) { // also look for PAS values
    if(!Shielding.ParseIndices(argv[1])) return 1;
    if(!ShieldingPAS.ParseIndices(argv[1])) return 1;
    if (!ShieldingFragments.AllocateMatrix(Shielding.Header, Shielding.MatrixCounter, Shielding.RowCounter, Shielding.ColumnCounter)) return 1;
    if (!ShieldingPASFragments.AllocateMatrix(ShieldingPAS.Header, ShieldingPAS.MatrixCounter, ShieldingPAS.RowCounter, ShieldingPAS.ColumnCounter)) return 1;
    if(!ShieldingFragments.ParseIndices(argv[1])) return 1;
    if(!ShieldingPASFragments.ParseIndices(argv[1])) return 1;
    if(!Chi.ParseIndices(argv[1])) return 1;
    if(!ChiPAS.ParseIndices(argv[1])) return 1;
    if (!ChiFragments.AllocateMatrix(Chi.Header, Chi.MatrixCounter, Chi.RowCounter, Chi.ColumnCounter)) return 1;
    if (!ChiPASFragments.AllocateMatrix(ChiPAS.Header, ChiPAS.MatrixCounter, ChiPAS.RowCounter, ChiPAS.ColumnCounter)) return 1;
    if(!ChiFragments.ParseIndices(argv[1])) return 1;
    if(!ChiPASFragments.ParseIndices(argv[1])) return 1;
  }
  // ---------- Parse the KeySets into an array ---------------
  {
    std::stringstream filename;
    filename << FRAGMENTPREFIX << KEYSETFILE;
    if (!KeySet.ParseKeySets(argv[1], filename.str(), Force.MatrixCounter)) return 1;
  }
  if (!KeySet.ParseManyBodyTerms()) return 1;
  // ---------- Parse fragment files created by 'joiner' into an array -------------
  if (!EnergyFragments.ParseFragmentMatrix(argv[1], dir, EnergyFragmentSuffix,0,0)) return 1;
  if (!NoHCorrection) 
    HcorrectionFragments.ParseFragmentMatrix(argv[1], dir, HcorrectionFragmentSuffix,0,0);
  if (!ForceFragments.ParseFragmentMatrix(argv[1], dir, ForceFragmentSuffix,0,0)) return 1;
  if (!NoHessian)
    if (!HessianFragments.ParseFragmentMatrix(argv[1], dir, HessianFragmentSuffix,0,0)) return 1;
  if (periode != NULL) { // also look for PAS values
    if (!ShieldingFragments.ParseFragmentMatrix(argv[1], dir, ShieldingFragmentSuffix, 1, 0)) return 1;
    if (!ShieldingPASFragments.ParseFragmentMatrix(argv[1], dir, ShieldingPASFragmentSuffix, 1, 0)) return 1;
    if (!ChiFragments.ParseFragmentMatrix(argv[1], dir, ChiFragmentSuffix, 1, 0)) return 1;
    if (!ChiPASFragments.ParseFragmentMatrix(argv[1], dir, ChiPASFragmentSuffix, 1, 0)) return 1;
  }
  // +++++++++++++++ TESTING ++++++++++++++++++++++++++++++
  // print energy and forces to file
  filename.str("");
  filename << argv[3] << "/" << "energy-forces.all";
  output.open(filename.str().c_str(), ios::out);
  output << endl << "Total Energy" << endl << "==============" << endl << Energy.Header[Energy.MatrixCounter] << endl;
  for(int j=0;j MYEPSILON)
          output2 << "\t" << scientific << Time.Matrix[ Time.MatrixCounter ][ Time.RowCounter[Time.MatrixCounter]-1 ][k] / Time.Matrix[ Time.MatrixCounter ][ Time.RowCounter[Time.MatrixCounter] ][k];
        else
          output2 << "\t" << scientific << Time.Matrix[ Time.MatrixCounter ][ Time.RowCounter[Time.MatrixCounter]-1 ][k];
      }
      output << endl;
      output2 << endl;
    }
    output.close();
    output2.close();
  }
  if (!NoHessian) {
    // +++++++++++++++++++++++++++++++++++++++ Plotting deviation in hessian to full QM
    if (!CreateDataDeltaHessianOrderPerAtom(Hessian, HessianFragments, KeySet, argv[3], "DeltaHessian_xx-Order", "Plot of error between approximated hessian and full hessian versus the Bond Order", datum)) return 1;
    if (!CreateDataDeltaFrobeniusOrderPerAtom(Hessian, HessianFragments, KeySet, argv[3], "DeltaFrobeniusHessian_xx-Order", "Plot of error between approximated hessian and full hessian in the frobenius norm versus the Bond Order", datum)) return 1;
    // ++++++++++++++++++++++++++++++++++++++Plotting Hessian vs. Order
    if (!CreateDataHessianOrderPerAtom(HessianFragments, KeySet, argv[3], "Hessian_xx-Order", "Plot of approximated hessian versus the Bond Order", datum)) return 1;
    if (!AppendOutputFile(output, argv[3], "Hessian_xx-Order.dat" )) return false;
    output << endl << "# Full" << endl;
    for(int j=0;j1) && (k<6))? 1.e6 : 1.) << "\t";
      output << endl;
    }
    output.close();
    if (!CreateDataDeltaForcesOrderPerAtom(ChiPAS, ChiPASFragments, KeySet, argv[3], "DeltaChisPAS-Order", "Plot of error between approximated Chis and full Chis versus the Bond Order", datum)) return 1;
    if (!CreateDataForcesOrderPerAtom(ChiPASFragments, KeySet, argv[3], "ChisPAS-Order", "Plot of approximated Chis versus the Bond Order", datum)) return 1;
    if (!AppendOutputFile(output, argv[3], "ChisPAS-Order.dat" )) return false;
    output << endl << "# Full" << endl;
    for(int j=0;j1) && (k<6))? 1.e6 : 1.) << "\t";
      output << endl;
    }
    output.close();
  }
  // +++++++++++++++++++++++++++++++++++++++ Plotting deviation in energy to full QM
  if (!CreateDataDeltaEnergyOrder(Energy, EnergyFragments, KeySet, argv[3], "DeltaEnergies-Order", "Plot of error between approximated and full energies energies versus the Bond Order", datum)) return 1;
  // +++++++++++++++++++++++++++++++++++Plotting Energies vs. Order
  if (!CreateDataEnergyOrder(EnergyFragments, KeySet, argv[3], "Energies-Order", "Plot of approximated energies versus the Bond Order", datum)) return 1;
  // +++++++++++++++++++++++++++++++++++++++ Plotting deviation in forces to full QM
  if (!CreateDataDeltaForcesOrderPerAtom(Force, ForceFragments, KeySet, argv[3], "DeltaForces-Order", "Plot of error between approximated forces and full forces versus the Bond Order", datum)) return 1;
  // min force
  if (!CreateDataDeltaForcesOrder(Force, ForceFragments, KeySet, argv[3], "DeltaMinForces-Order", "Plot of min error between approximated forces and full forces versus the Bond Order", datum, CreateMinimumForce)) return 1;
  // mean force
  if (!CreateDataDeltaForcesOrder(Force, ForceFragments, KeySet, argv[3], "DeltaMeanForces-Order", "Plot of mean error between approximated forces and full forces versus the Bond Order", datum, CreateMeanForce)) return 1;
  // max force
  if (!CreateDataDeltaForcesOrder(Force, ForceFragments, KeySet, argv[3], "DeltaMaxForces-Order", "Plot of max error between approximated forces and full forces versus the Bond Order", datum, CreateMaximumForce)) return 1;
  // ++++++++++++++++++++++++++++++++++++++Plotting Forces vs. Order
  if (!CreateDataForcesOrderPerAtom(ForceFragments, KeySet, argv[3], "Forces-Order", "Plot of approximated forces versus the Bond Order", datum)) return 1;
  if (!AppendOutputFile(output, argv[3], "Forces-Order.dat" )) return false;
  output << endl << "# Full" << endl;
  for(int j=0;j