/*
 * 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 .
 */
/*
 * SubsetMap.cpp
 *
 *  Created on: Jul 3, 2012
 *      Author: heber
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
//#include "CodePatterns/MemDebug.hpp"
#include "SubsetMap.hpp"
#include 
#include 
#include 
#include 
#include 
#include 
#include "CodePatterns/Assert.hpp"
#include "CodePatterns/Log.hpp"
SubsetMap::SubsetMap(IndexSetContainer &_container) :
  tempset(new IndexSet())
{
  /// go through all IndexSets
  const IndexSetContainer::Container_t &container = _container.getContainer();
  for (IndexSetContainer::Container_t::const_iterator iter = container.begin();
      iter != container.end(); ++iter) {
    // check whether its the super set
    if (*iter == _container.getSuperSet()) {
      // place all other sets as its subsets
      std::vector returnsets;
      returnsets.reserve(container.size());
      for (IndexSetContainer::Container_t::const_iterator insertiter = container.begin();
            insertiter != container.end(); ++insertiter)
        if (iter != insertiter) {
          LOG(2, "INFO: Current subset is " << **insertiter << " for super set.");
          ASSERT( _container.getSuperSet()->contains(**insertiter),
              "SubsetMap::SubsetMap() - "+toString(**insertiter)+" is not contained in super set "
              +toString(*_container.getSuperSet())+".");
          returnsets.push_back(**insertiter);
        }
      Lookup.insert( std::make_pair(*iter, IndexSetContainer::ptr(new IndexSetContainer(returnsets))) );
    } else {
      gatherSubsets(*iter);
    }
  }
}
void SubsetMap::gatherSubsets(const IndexSet_ptr &ptr) {
  // get the sets size and the size of the power sets
  const size_t SetSize = ptr->size();
  LOG(2, "DEBUG: Current set to gather all subsets for is " << *ptr << " with size " << SetSize << ".");
  const size_t NoPowerSubsets = getNoPowerSets(SetSize);
  LOG(2, "DEBUG: There are " << NoPowerSubsets << " sets to check for containment.");
  std::vector returnsets;
  // sets is the number of possible subsets to check: NotContained means still
  // needs check or is not contained; Contained means is contained, or has
  // already been treated via a super(sub)set.
  typedef std::vector StateOfSubsets;
  StateOfSubsets sets( NoPowerSubsets, NotContained);
  // we have to traverse backwards, i.e. from greatest subsets to smallest to
  // make the best possible use of the subset knowledge we already have for these
  // subsets of the subset.
  // WARNING: last set is the set itself which must not be in IndexSetContainer due to
  // cyclic structure which shared_ptr cannot resolve.
  StateOfSubsets::reverse_iterator iter = sets.rbegin()+1;
  for (;iter != sets.rend(); iter = std::find(iter+1, sets.rend(), NotContained)) {
    // get IndexSet (get index via distance)
    // NOTE: reverse iterator naturally give reverse distance, hence flip both arguments
    // also, reverse iterator is off by one, see ASSERT
    const size_t index = std::distance(iter, sets.rend())-1;
    ASSERT( sets.at(index) == *iter,
        "SubsetMap::gatherSubsets() - distance from begin to iterator is not correct.");
    *tempset = getSubset(*ptr, index );
    LOG(3, "DEBUG: Current subset is " << *tempset << " at " << index << ".");
    // check whether IndexSet is contained and known
    if (ptr->contains(*tempset)) {
      // mark as contained
      *iter = Contained;
      LOG(4, "DEBUG: Marking subset as Contained.");
      if (Lookup.count(tempset)) {
        LOG(3, "DEBUG: Subset is present in Lookup, adding.");
        returnsets.push_back(*tempset);
        // if so, also add its contained sets and mark them out
        const IndexSetContainer::Container_t &container = getSubsets(tempset)->getContainer();
        if (container.size()) {
          LOG(3, "DEBUG: Subset is also present in Lookup, adding all its subsets");
          for(IndexSetContainer::Container_t::const_iterator containeriter = container.begin();
              containeriter != container.end();
              ++containeriter) {
            const size_t subindex = getPowerSetIndex(ptr, **containeriter);
            LOG(5, "INFO: Current subset of subset is " << **containeriter << " at " << subindex << ".");
            if (sets[subindex] != Contained) {
              LOG(6, "DEBUG: Setting power subset to Contained.");
              sets[subindex] = Contained;
              returnsets.push_back(**containeriter);
            }
          }
        }
      }
    }
  }
  LOG(2, "DEBUG: Adding " << returnsets.size() << " sets to Lookup for set " << *ptr << ".");
  Lookup.insert( std::make_pair(ptr, IndexSetContainer::ptr(new IndexSetContainer(returnsets))) );
}
const size_t SubsetMap::getPowerSetIndex(const IndexSet_ptr &ptr, const IndexSet &subset) {
  ASSERT( (ptr->size() < MAX_SETSIZE) && (subset.size() < MAX_SETSIZE),
      "SubsetMap::getPowerSetIndex() - power sets of subsets must not be bigger than "
      +toString((int)MAX_SETSIZE)+".");
  ASSERT( ptr->contains(subset),
      "SubsetMap::getPowerSetIndex() - "+(toString(*ptr))+" does not contain "
          +toString(subset)+".");
  std::bitset bits(0);
  // go through each and search for indices present in both
  IndexSet::iterator indexiter = ptr->begin();
  IndexSet::iterator subindexiter = subset.begin();
  for (; subindexiter != subset.end();) {
    if (*indexiter == *subindexiter) {
      // if matching, set bit and advance both iterators
      bits.set( (size_t)std::distance(ptr->begin(), indexiter) );
      ++indexiter;
      ++subindexiter;
      // if not, advance the iterator lacking behind
    } else if ( *indexiter < *subindexiter) {
      ++indexiter;
    } else {
      ++subindexiter;
    }
  }
  return bits.to_ulong();
}
IndexSet SubsetMap::getSubset(const IndexSet &_set, size_t PowerSetNo) {
  ASSERT( _set.size() < MAX_SETSIZE,
      "SubsetMap::getSubset() - power sets of subsets must not be bigger than "
      +toString((int)MAX_SETSIZE)+".");
  ASSERT((PowerSetNo >= 0) && (PowerSetNo < getNoPowerSets(_set.size())),
      "SubsetMap::getSubset() - Power set index "+toString(PowerSetNo)+" is out of range.");
  std::bitset bits(PowerSetNo);
  // place index into container with random access
  std::vector indices(_set.begin(), _set.end());
  IndexSet indexset;
  // go through each bit and insert if 1, not if 0
  for (size_t index=0; index < indices.size(); ++index)
    if (bits[index])
      indexset.insert(indices[index]);
  return indexset;
}
size_t SubsetMap::getMaximumSetLevel() const
{
  if (Lookup.empty())
    return 0;
  // last one is super set
  Lookup_t::const_iterator iter = --Lookup.end();
  return iter->first->size();
}