// $Id: ReferenceSet.h 1365 2024-06-28 20:00:02Z ge $
/// \file ReferenceSet.h
/// \brief contains the ReferenceSet class
///
/// $Revision: 1365 $
/// \author Gerald Weber

#ifndef GBC_REFERENCESET_H
#define GBC_REFERENCESET_H "$Id: ReferenceSet.h 1365 2024-06-28 20:00:02Z ge $"
#include <sstream>
#include "ErrorCodes.h"

namespace gbc {

using namespace gbc;

template<class _DequeTp, typename _CountTp=int>
class ReferenceSet: public std::map<typename _DequeTp::pair_type,_CountTp>
  {
  public:
    typedef typename _DequeTp::pair_type pair_type;
    typedef typename _DequeTp::base_pair_type base_pair_type;
    typedef std::map<pair_type,_CountTp> map_type;
    bool Debug;

  typename base_pair_type::SymmetryActionsType Symmetry_action;    ///< Flags which symmetry action will be performed

  
  ReferenceSet(void): Debug(false), Symmetry_action(base_pair_type::simplify_symmetry) {};

  ReferenceSet(bool sym): Debug(false)
  {
  if (sym) Symmetry_action=base_pair_type::simplify_symmetry;
  else     Symmetry_action=base_pair_type::do_not_simplify_symmetry;
  }
  
  inline void reference_insert_reduced(pair_type bpn)
    {
    CERR_DDEBUG(DRES_BPNSR) << "before symmetry action bpn=" << bpn << std::endl;
    if (Symmetry_action == base_pair_type::simplify_symmetry) bpn.reduce_to_smallest_symmetry();
    CERR_DDEBUG(DRES_BPNSR) << "after symmetry action bpn=" << bpn << std::endl;
    this->reference_insert(bpn);
    }
  
  inline void reference_insert(pair_type bpn, _CountTp count=1)
    {
    std::pair<typename map_type::iterator,bool> nn;
    typename map_type::value_type test(bpn,count);
    nn=this->insert(test);
//     typename map_type::key_compare cmp=this->key_comp();
    if (nn.second) CERR_DDEBUG(DRES_NEWBPN) << " new bpn=" << nn.first->first << std::endl;
    if (!nn.second) //means not new
      nn.first->second += count;
    }

  inline void insert_from_map(map_type &mp)
    {
    typename map_type::const_iterator in;
    for(in=mp.begin(); in != mp.end(); ++in)
      {
      this->reference_insert(in->first,in->second);
      }
    }

  inline void analyse(const _DequeTp& dq)
   {
   typename _DequeTp::const_iterator it;
   for(it=dq.begin(); it != dq.end(); ++it) this->reference_insert_reduced(*it);
   }

 inline _CountTp count(pair_type bpn) const
    {
    if (Symmetry_action == base_pair_type::simplify_symmetry) bpn.reduce_to_smallest_symmetry();
    typename map_type::const_iterator it=this->find(bpn);
    if (it != this->end()) 
      {
      return it->second;
      }
    return _CountTp();
    }

  inline std::string formatted_string(void) const
    {
    std::stringstream out;
    for (auto it: *this)
      {
      out << it.first << "=" << it.second << " ";
      }
    return out.str();
    }

  inline _CountTp operator[](const pair_type& bpn) const
    {return count(bpn);}

  inline _CountTp operator[](std::string nm) const
    {
    pair_type bp(nm);
    return count(bp);
    }
    
  std::set<std::string> string_set(void)
    {
    std::set<std::string> the_set;
    for (auto it: *this) the_set.insert((std::string)it.first);
    return the_set;
    }

  friend std::ostream& operator<<(std::ostream& out, const ReferenceSet& ref)
    {
    typename ReferenceSet::const_iterator it;
    for (it=ref.begin(); it != ref.end(); it++)
      {
      out << it->first << "=" << it->second << " ";
      }
    out << std::endl;
    return out;
    }

  };
};//ends namespace
#endif
