/*
 * Project: MoleCuilder
 * Description: creates and alters molecular systems
 * Copyright (C)  2010-2012 University of Bonn. All rights reserved.
 * Copyright (C)  2013 Frederik Heber. 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 .
 */
/*
 * KeySetsContainer.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 
#include "CodePatterns/Assert.hpp"
#include "CodePatterns/Log.hpp"
#include "Fragmentation/defs.hpp"
#include "Fragmentation/helpers.hpp"
#include "Helpers/defs.hpp"
#include "KeySetsContainer.hpp"
/** Constructor of KeySetsContainer class.
 */
KeySetsContainer::KeySetsContainer() :
  FragmentCounter(0),
  Order(0),
  FragmentsPerOrder(0)
{};
/** Destructor of KeySetsContainer class.
 */
KeySetsContainer::~KeySetsContainer() {
//  for(int i=FragmentCounter;i--;)
//    delete[](KeySets[i]);
//  for(int i=Order;i--;)
//    delete[](OrderSet[i]);
//  delete[](KeySets);
//  delete[](OrderSet);
//  delete[](AtomCounter);
//  delete[](FragmentsPerOrder);
};
/** Parsing KeySets into array.
 * \param *name directory with keyset file
 * \param *ACounter number of atoms per fragment
 * \param FCounter number of fragments
 * \return parsing succesful
 */
bool KeySetsContainer::ParseKeySets(const std::string path, const std::string name, const int FCounter) {
  ifstream input;
  //char *FragmentNumber = NULL;
  stringstream file;
  char filename[1023];
  FragmentCounter = FCounter;
  KeySets.resize(FragmentCounter);
  AtomCounter.resize(FragmentCounter);
  // open file
  file << path << "/" << name;
  input.open(file.str().c_str(), ios::in);
  if (input.fail()) {
    LOG(0, endl << "KeySetsContainer::ParseKeySets: Unable to open " << file.str() << ", is the directory correct?");
    return false;
  }
  // parse each line, i.e. get the index set per fragment
  LOG(0, "Parsing key sets.");
  for(int i=0;(i > tokenizer;
    boost::char_separator sep(" \t");
    tokenizer tok(line, sep);
    for(tokenizer::iterator beg=tok.begin();
        beg != tok.end();++beg){
      const int tempvalue = boost::lexical_cast(*beg);
      KeySets[i].push_back(tempvalue);
//      set_output << " " << KeySets[i].back();
    }
//    LOG(2, "DEBUG: Scanned keys are '" << set_output.str() << "'.");
    AtomCounter[i] = KeySets[i].size();
//    {
//      std::stringstream output;
//      FragmentNumber = FixedDigitNumber(FragmentCounter, i);
//      output << FRAGMENTPREFIX << FragmentNumber << "[" << AtomCounter[i] << "]:" << set_output.str();
//      delete[](FragmentNumber);
//      LOG(0, output.str());
//    }
  }
  input.close();
  return true;
};
/** Parse many body terms, associating each fragment to a certain bond order.
 * \return parsing succesful
 */
bool KeySetsContainer::ParseManyBodyTerms()
{
  int Counter;
  LOG(0, "Creating Fragment terms.");
  // scan through all to determine maximum order
  Order=0;
  for(int i=FragmentCounter;i--;) {
    Counter=0;
    for(int j=AtomCounter[i];j--;)
      if (KeySets[i][j] != -1)
        Counter++;
    if (Counter > Order)
      Order = Counter;
  }
  LOG(0, "Found Order is " << Order << ".");
  // scan through all to determine fragments per order
  FragmentsPerOrder.resize(Order);
//  for(int i=Order;i--;)
//    FragmentsPerOrder[i] = 0;
  for(int i=FragmentCounter;i--;) {
    Counter=0;
    for(int j=AtomCounter[i];j--;)
      if (KeySets[i][j] != -1)
        Counter++;
    FragmentsPerOrder[Counter-1]++;
  }
  for(int i=0;i FragmentCounter) || (SmallerSet > FragmentCounter)) // index out of bounds
    return false;
  for(int i=AtomCounter[SmallerSet];i--;) {
    intermediate = false;
    for (int j=AtomCounter[GreaterSet];j--;)
      intermediate = (intermediate || ((KeySets[SmallerSet][i] == KeySets[GreaterSet][j]) || (KeySets[SmallerSet][i] == -1)));
    result = result && intermediate;
  }
  return result;
};
/** Comparison operator for class KeySetsContainer.
 *
 * @param other instance to compare to
 * @return true - both instances are the same in each member variable.
 */
bool KeySetsContainer::operator==(const KeySetsContainer &other) const
{
  // compare KeySets
  if (KeySets != other.KeySets)
    return false;
  // compare OrderSet
  if (OrderSet != other.OrderSet)
    return false;
  // compare AtomCounter
  if (AtomCounter != other.AtomCounter)
    return false;
  // compare FragmentCounter
  if (FragmentCounter != other.FragmentCounter)
    return false;
  // compare Order
  if (Order != other.Order)
    return false;
  // compare FragmentsPerOrder
  if (FragmentsPerOrder != other.FragmentsPerOrder)
    return false;
  return true;
}
void KeySetsContainer::clear()
{
  KeySets.clear();
  OrderSet.clear();
  AtomCounter.clear();
  FragmentCounter = 0;
  Order = 0;
  FragmentsPerOrder.clear();
}
void KeySetsContainer::insert(const KeySetsContainer &other)
{
  // append sets and their atom count
  KeySets.insert(KeySets.end(), other.KeySets.begin(), other.KeySets.end());
  AtomCounter.insert(AtomCounter.end(), other.AtomCounter.begin(), other.AtomCounter.end());
  // ASSUME: that the fragments inside other are number consecutively
  // find the smallest number
  int smallest_index = std::numeric_limits::max();
  for(size_t i=0;i= OrderSet.size() )
        OrderSet.resize(i+1);
      OrderSet[i].push_back((*iter)+FragmentCounter-smallest_index);
      if (i >= FragmentsPerOrder.size())
        FragmentsPerOrder.resize(i+1);
      ++(FragmentsPerOrder[i]);
    }
    nr_fragments += other.OrderSet[i].size();
  }
  Order = std::max(Order, other.Order);
  // the following assert must not necessarily be true because of cycles: 
  // Order 4 with added cycles is still order 4.
  //ASSERT( Order == (int)OrderSet.size(),
  //    "KeySetsContainer::insert() - order not max of either container?");
  FragmentCounter += nr_fragments;
}
void KeySetsContainer::insert(const IntVector &indices, const size_t order)
{
  KeySets.push_back(indices);
  AtomCounter.push_back(indices.size());
  if (order > OrderSet.size() )
    OrderSet.resize(order);
  OrderSet[order-1].push_back(FragmentCounter);
  if (order > FragmentsPerOrder.size())
    FragmentsPerOrder.resize(order);
  ++(FragmentsPerOrder[order-1]);
  ++FragmentCounter;
}