/*
 * Project: MoleCuilder
 * Description: creates and alters molecular systems
 * Copyright (C)  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 .
 */
/*
 * IndexSetContainer.cpp
 *
 *  Created on: Aug 2, 2012
 *      Author: heber
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
#include "CodePatterns/MemDebug.hpp"
#include "IndexSetContainer.hpp"
#include 
#include "CodePatterns/Log.hpp"
#include "Fragmentation/Summation/IndexSet.hpp"
#include "Fragmentation/KeySetsContainer.hpp"
IndexSet::ptr IndexSetContainer::createSuperSet(const KeySetsContainer &_keysets) const
{
  // create superset
  IndexSet::ptr _AllIndices(new IndexSet);
  for (KeySetsContainer::ArrayOfIntVectors::const_iterator iter = _keysets.KeySets.begin();
      iter != _keysets.KeySets.end(); ++iter)
    for(KeySetsContainer::IntVector::const_iterator keyiter = (*iter).begin();
        keyiter != (*iter).end(); ++keyiter) {
      if (*keyiter != -1)
        _AllIndices->insert((Index_t)*keyiter);
    }
  LOG(2, "DEBUG: AllIndices is " << *_AllIndices << ".");
  return _AllIndices;
}
IndexSet::ptr IndexSetContainer::createSuperSet(const std::vector &_container) const
{
  // create superset
  IndexSet::ptr _AllIndices(new IndexSet);
  for (std::vector::const_iterator iter = _container.begin();
      iter != _container.end(); ++iter)
    for(IndexSet::const_iterator keyiter = (*iter).begin();
        keyiter != (*iter).end(); ++keyiter) {
      if (*keyiter != -1)
        _AllIndices->insert((Index_t)*keyiter);
    }
  LOG(2, "DEBUG: AllIndices is " << *_AllIndices << ".");
  return _AllIndices;
}
IndexSet::ptr IndexSetContainer::createSuperSet(const std::vector &_container) const
{
  // create superset
  IndexSet::ptr _AllIndices(new IndexSet);
  for (std::vector::const_iterator iter = _container.begin();
      iter != _container.end(); ++iter)
    for(IndexSet::const_iterator keyiter = (*iter)->begin();
        keyiter != (*iter)->end(); ++keyiter) {
      if (*keyiter != -1)
        _AllIndices->insert((Index_t)*keyiter);
    }
  LOG(2, "DEBUG: AllIndices is " << *_AllIndices << ".");
  return _AllIndices;
}
IndexSetContainer::IndexSetContainer(const KeySetsContainer &_keysets) :
     AllIndices(createSuperSet(_keysets))
{
  const size_t SupersetSize = AllIndices->size();
  // create container with all keysets
  for (KeySetsContainer::ArrayOfIntVectors::const_iterator iter = _keysets.KeySets.begin();
      iter != _keysets.KeySets.end(); ++iter) {
    // create the IndexSets ...
    IndexSet tempset;
    for(KeySetsContainer::IntVector::const_iterator keyiter = (*iter).begin();
        keyiter != (*iter).end(); ++keyiter)
      if (*keyiter != -1)
        tempset.insert((Index_t)*keyiter);
    // ... and insert, making sure that super set is AllIndices when present
    if (tempset.size() < SupersetSize)
      insert(tempset);
    else
      insert(AllIndices);
  }
}
size_t IndexSetContainer::countSetsTillLevel(const size_t level) const
{
  // check if any sets are present
  if (getContainer().empty())
    return 0;
  // define ends of interval
  SortedVector::Container_t::const_iterator start = getContainer().begin();
  SortedVector::Container_t::const_iterator end = --getContainer().end();
  // check whether all are smaller
  if ((*end)->size() <= level)
    return getContainer().size();
  // check whether all are larger
  if ((*start)->size() > level)
    return 0;
  // continue as long as interval has more than one element
  while (std::distance(start, end) > 1) {
    // create iterator in between
    SortedVector::Container_t::const_iterator midpoint = start;
    std::advance( midpoint, std::distance(start, end)/2 );
    // check iter and half the interval
    if ((*midpoint)->size() <= level)
      start = midpoint;
    else
      end = midpoint;
  }
  return std::distance(getContainer().begin(), end);
}