//////////////////////////////////////////////////////////////////////////
// SwitchTable.hh
// produced: 09/03/2020 jr
// (based on "Parallel Enumeration of Triangulations" by
// Jordan, Joswig, and Kastner, 2018)
/////////////////////////////////////////////////////////////////////////
#ifndef SWITCHTABLE_HH
#define SWITCHTABLE_HH

#include <assert.h>
#include <iostream>
#include <iterator>
#include <algorithm>

#include <vector>

#include "Global.hh"
#include "ContainerIO.hh"
#include "LabelSet.hh"
#include "Symmetry.hh"
#include "Message.hh"

namespace topcom {

  class switch_table_row_type : public std::vector<Symmetry> {
    friend std::istream& operator>>(std::istream& ist, switch_table_row_type& str) {
      MessageStreams::debug() << message::lock
			      << "reading dense data of switch_table_row_type:\n"
			      << message::unlock;
      return ist >> static_cast<std::vector<Symmetry>&>(str);
      MessageStreams::debug() << message::lock
			      << "read " << str << "\n"
			      << message::unlock;
    }
    friend std::ostream& operator<<(std::ostream& ost, const switch_table_row_type& str) {
      return ost << static_cast<const std::vector<Symmetry>&>(str);
    }
  public:
    inline switch_table_row_type() :
      std::vector<Symmetry>() {}
    inline switch_table_row_type(const parameter_type n) :
      std::vector<Symmetry>(n, Symmetry(0)) {}
  };

  class switch_selection_type : public std::vector<const Symmetry*> {
    friend std::ostream& operator<<(std::ostream& ost, const switch_selection_type ss) {
      for (switch_selection_type::const_iterator iter = ss.begin();
	   iter != ss.end();
	   ++iter) {
	ost << **iter << " ";
      }
      return ost;
    }
  public:
    inline switch_selection_type() :
      std::vector<const Symmetry*>() {}
  };

  typedef std::vector<switch_table_row_type> switch_table_data_type;
  
  template <class IndexSet, comparison_mode_type mode>
  class SwitchTable : public switch_table_data_type {
  public:
    typedef comparison_mode_type              mode_type;
    typedef typename IndexSet::const_iterator IndexSet_const_iterator;
  private:
    parameter_type                _n;
    IndexSet                      _non_trivial_rowset;
    std::vector<IndexSet>         _non_trivial_colsetvec;
    Symmetry                      _identity;
    size_type                     _order;
    mutable size_type             _seecount; // can be mutable because it is only statistics
  private:
    SwitchTable() = delete;
  public:
    
    // constructors:
    SwitchTable(const parameter_type); // contains only the identity
    SwitchTable(const parameter_type,
		const symmetry_collectordata&); // built from generators
    SwitchTable(const SymmetryGroup&); // built from symmetry group
    inline SwitchTable(const SwitchTable&); // copied
  
    // destructor:
    inline ~SwitchTable();
  
    // assignment:
    inline SwitchTable& operator=(const SwitchTable&);

    // accessors:
    inline parameter_type                     n() const;
    inline const IndexSet&                    non_trivial_rowset() const;
    inline const std::vector<IndexSet>&       non_trivial_colsetvec() const;
    inline const Symmetry&                    identity() const;
    inline size_type                          order() const;
    inline size_type                          seecount() const;
    // functionality:
    // standard functions:
    bool                                empty          () const;
    size_type                           compute_order  () const;
    bool                                is_in_group    (const Symmetry&) const;
    bool                                add_to_group   (const Symmetry&);
    size_type                           stabilizer_card(const IntegerSet&) const;
    size_type                           stabilizer_card(const IntegerSet64&) const;
    size_type                           orbit_size     (const IntegerSet&) const;
    size_type                           orbit_size     (const IntegerSet64&) const;
    symmetry_collectordata              all_switches   () const;
    symmetry_collectordata              all_switches   (const parameter_type) const;
    void                                gen_symmetries (const Symmetry&,
							const parameter_type,
							symmetry_iterdata&) const;
    symmetry_iterdata                   all_symmetries () const;
    symmetry_iterdata                   all_symmetries_bfs() const;
    
    // extract those switches that may colex-increase a IndexSet:
    switch_selection_type               canonicalizing_switchptrs(const parameter_type,
								  const IndexSet&) const;
    switch_selection_type               neutral_switchptrs(const parameter_type i,
							   const IndexSet& ls) const;
    // Jordan-Joswig-Kastner specialization:
    bool                                not_canonical  (const parameter_type,
							const IndexSet&,
							const IndexSet&) const;
    inline bool                         not_canonical  (const IndexSet&) const;
    // combination of Jordan-Joswig-Kastner with Pech-Reichard:
    bool                                not_canonicalPR(const parameter_type,
							const parameter_type,
							const IndexSet&,
							const IndexSet&) const;
    inline bool                         not_canonicalPR(const IndexSet&) const;
    bool                                lex_decreases  (const IndexSet&) const;
    bool                                colex_increases(const IndexSet&) const;

    // scan switch products stabilizing a subset:
    switch_selection_type               stabilizing_switchptrs(const parameter_type,
							       const IndexSet&,
							       const IndexSet&) const;
    size_type                           count_stabilizing_switchproducts(const parameter_type,
									 const IndexSet&,
									 const IndexSet&) const;
   
    // stream input/output:
    inline std::istream& read(std::istream&);
    inline std::istream& sparse_read(std::istream&);
    template <class StreamIndexSet, mode_type streammode>
    inline friend std::istream& operator>>(std::istream&, SwitchTable<StreamIndexSet, streammode>&);
    inline Message& write(Message&) const;
    inline std::ostream& write(std::ostream&) const;
    inline Message& sparse_write(Message&) const;
    inline std::ostream& sparse_write(std::ostream&) const;
    inline Message& pretty_print(Message&) const;
    inline std::ostream& pretty_print(std::ostream&) const;
    inline Message& write_sorted_generators(Message&) const;
    inline std::ostream& write_sorted_generators(std::ostream&) const;
    template <class StreamIndexSet, mode_type streammode>
    inline friend std::ostream& operator<<(std::ostream&, const SwitchTable<StreamIndexSet, streammode>&);

  private:
    
    // internal methods:
    inline parameter_type _elem(const parameter_type) const;
    inline parameter_type _mincol(const parameter_type) const;
    inline parameter_type _maxcol(const parameter_type) const;
    inline bool _compare_subsets(const IndexSet&, const IndexSet&) const;
    bool _add_symmetry(const Symmetry&);
  };

  // forward template specializations:
  template <>
  inline parameter_type SwitchTable<IntegerSet, lexmin_mode>::_elem(const parameter_type) const;
  
  template <>
  inline parameter_type SwitchTable<IntegerSet, colexmax_mode>::_elem(const parameter_type) const;

  template <>
  inline parameter_type SwitchTable<IntegerSet, lexmin_mode>::_mincol(const parameter_type) const;
  
  template <>
  inline parameter_type SwitchTable<IntegerSet, colexmax_mode>::_mincol(const parameter_type) const;

  template <>
  inline parameter_type SwitchTable<IntegerSet, lexmin_mode>::_maxcol(const parameter_type) const;
  
  template <>
  inline parameter_type SwitchTable<IntegerSet, colexmax_mode>::_maxcol(const parameter_type) const;

  template <>
  inline bool SwitchTable<IntegerSet, lexmin_mode>::_compare_subsets(const IntegerSet&, const IntegerSet&) const;
  
  template <>
  inline bool SwitchTable<IntegerSet, colexmax_mode>::_compare_subsets(const IntegerSet&, const IntegerSet&) const;

  template <>
  inline parameter_type SwitchTable<IntegerSet64, lexmin_mode>::_elem(const parameter_type) const;
  
  template <>
  inline parameter_type SwitchTable<IntegerSet64, colexmax_mode>::_elem(const parameter_type) const;

  template <>
  inline parameter_type SwitchTable<IntegerSet64, lexmin_mode>::_mincol(const parameter_type) const;
  
  template <>
  inline parameter_type SwitchTable<IntegerSet64, colexmax_mode>::_mincol(const parameter_type) const;

  template <>
  inline parameter_type SwitchTable<IntegerSet64, lexmin_mode>::_maxcol(const parameter_type) const;
  
  template <>
  inline parameter_type SwitchTable<IntegerSet64, colexmax_mode>::_maxcol(const parameter_type) const;

  template <>
  inline bool SwitchTable<IntegerSet64, lexmin_mode>::_compare_subsets(const IntegerSet64&, const IntegerSet64&) const;
  
  template <>
  inline bool SwitchTable<IntegerSet64, colexmax_mode>::_compare_subsets(const IntegerSet64&, const IntegerSet64&) const;

  // constructors:
  template <class IndexSet, comparison_mode_type mode>
  inline SwitchTable<IndexSet, mode>::SwitchTable(const SwitchTable& st) :
    switch_table_data_type(st),
    _n(st._n),
    _non_trivial_rowset(st._non_trivial_rowset),
    _non_trivial_colsetvec(st._non_trivial_colsetvec),
    _identity(st._identity),
    _order(st._order) {
  }
  
  template <class IndexSet, comparison_mode_type mode>
  SwitchTable<IndexSet, mode>::SwitchTable(const parameter_type n) :
    switch_table_data_type(),
    _n(n),
    _non_trivial_rowset(),
    _non_trivial_colsetvec(n, IndexSet()),
    _identity(Symmetry(n)),
    _order(1UL) {
  }
     
  template <class IndexSet, comparison_mode_type mode>
  SwitchTable<IndexSet, mode>::SwitchTable(const parameter_type n,
					   const symmetry_collectordata& generators) :
    switch_table_data_type(),
    _n(n),
    _non_trivial_rowset(),
    _non_trivial_colsetvec(n, IndexSet()),
    _identity(Symmetry(n)),
    _order(1UL) {
    MessageStreams::debug() << message::lock
			    << "building switch table ..." << std::endl
			    << message::unlock;
    
    bool new_entries_found = false;
    
    // replace the previous entry wherever a lex-decreasing/colex-increasing switch exists:
    for (symmetry_collectordata::const_iterator geniter = generators.begin();
	 geniter != generators.end();
	 ++geniter) {
      if (_add_symmetry(*geniter)) {
	new_entries_found = true;
      }
    }
    
    while (new_entries_found) {
      new_entries_found = false;
      
      // replace the previous entry wherever a lex-decreasing/colex-increasing switch exists:
      for (symmetry_collectordata::const_iterator geniter = generators.begin();
	   geniter != generators.end();
	   ++geniter) {
	const Symmetry& generator(*geniter);
	for (IndexSet_const_iterator rowiter = _non_trivial_rowset.begin();
	     rowiter != _non_trivial_rowset.end();
	     ++rowiter) {
	  for (IndexSet_const_iterator coliter = _non_trivial_colsetvec.at(*rowiter).begin();
	       coliter != _non_trivial_colsetvec.at(*rowiter).end();
	       ++coliter) {
	    const Symmetry sym = this->at(*rowiter).at(*coliter) * generator;
	    if (add_to_group(sym)) {
	      new_entries_found = true;
	    }
	  }
	}
      }
    }
    _order = compute_order();
    if (CommandlineOptions::debug()) {

      // check whether each symmetry in the symmetry group is a product of switches
      // of the constructed switch table:
      MessageStreams::debug() << message::lock
			      << "SwitchTable::SwitchTable(const mode_type mode, const parameter_type& n): "
			      << "checking correctnes of switch table by generating complete group ..." << std::endl
			      << message::unlock;
      SymmetryGroup sg(n, generators, true);
      for (SymmetryGroup::const_iterator symiter = sg.begin();
	   symiter != sg.end();
	   ++symiter) {
	if (!this->is_in_group(*symiter)) {
	  MessageStreams::forced() << message::lock
				   << "SwitchTable::SwitchTable(const mode_type mode, const parameter_type& n): "
				   << "error - symmetry " << *symiter
				   << " not a product of switches in\n" << *this
				   << " - exiting" << std::endl
				   << message::unlock;
	  exit(1);
	}
      }
      MessageStreams::debug() << message::lock
			      << "... done: ok" << std::endl
			      << message::unlock;
    }
    if (empty()) {
      MessageStreams::verbose() << message::lock
				<< "switch table has no rows" << std::endl
				<< message::unlock;
    }
    else {
      MessageStreams::verbose() << message::lock
				<< "switch table has " << _non_trivial_rowset.max_elem() + 1
				<< " rows for a symmetry group of order " << _order
				<< " and degree " << _n << std::endl
				<< message::unlock;
      if (CommandlineOptions::verbose()) {
	MessageStreams::verbose() << message::lock
				<< "no. of non-trivial switches in rows:\n"
				<< message::unlock;
	for (IndexSet_const_iterator rowiter = _non_trivial_rowset.begin();
	     rowiter != _non_trivial_rowset.end();
	     ++rowiter) {
	  MessageStreams::verbose() << message::lock
				    << *rowiter
				    << ": "
				    << '(' << _non_trivial_colsetvec.at(*rowiter).card() << ") "
				    << message::unlock;
	  for (IndexSet_const_iterator coliter = _non_trivial_colsetvec.at(*rowiter).begin();
	       coliter != _non_trivial_colsetvec.at(*rowiter).end();
	       ++coliter) {
	    MessageStreams::debug() << message::lock
				    << *coliter << ':' << this->at(*rowiter).at(*coliter) << ','
				    << message::unlock;
	  }
	  MessageStreams::verbose() << message::lock
				    << '\n'
				    << message::unlock;
	}
      }
    }
    MessageStreams::debug() << message::lock
			    << "... done" << std::endl
			    << message::unlock;
  }
   
  template <class IndexSet, comparison_mode_type mode>
  SwitchTable<IndexSet, mode>::SwitchTable(const SymmetryGroup& sg) :
    switch_table_data_type(switch_table_data_type(sg.n(), switch_table_row_type(sg.n()))),
    _n(sg.n()),
    _non_trivial_rowset(),
    _non_trivial_colsetvec(sg.n(), IndexSet()),
    _identity(sg.identity()),
    _order(1UL) {
    MessageStreams::debug() << message::lock
			    << "building switch table ..." << std::endl
			    << message::unlock;
    
    if (!sg.complete()) {
      MessageStreams::forced() << message::lock
			       << "SwitchTable::SwitchTable(const mode_type mode, const SymmetryGroup& sg): "
			       << "symmetry group must be complete for switch table construction - exiting" << std::endl
			       << message::unlock;
      exit(1);
    }
    
    // replace the previous entry wherever a lex-decreasing/colex-increasing switch exists:
    for (SymmetryGroup::const_iterator iter = sg.begin();
	 iter != sg.end();
	 ++iter) {
      _add_symmetry(*iter);
    }
    if (CommandlineOptions::debug()) {
      
      // check whether each symmetry in the symmetry group is a product of switches
      // of the constructed switch table:
      for (SymmetryGroup::const_iterator symiter = sg.begin();
	   symiter != sg.end();
	   ++symiter) {
	if (!this->is_in_group(*symiter)) {
	  MessageStreams::forced() << message::lock
				   << "SwitchTable::SwitchTable(const mode_type mode, const parameter_type& n): "
				   << "error - symmetry " << *symiter
				   << " not a product of switches in " << *this
				   << " - exiting" << std::endl
				   << message::unlock;
	  exit(1);
	}
      }
    }
    _order = compute_order();
    if (empty()) {
      MessageStreams::verbose() << message::lock
				<< "switch table has no rows" << std::endl
				<< message::unlock;
    }
    else {
      MessageStreams::verbose() << message::lock
				<< "switch table has " << _non_trivial_rowset.max_elem()
				<< " rows for a symmetry group of order " << _order << std::endl
				<< message::unlock;
    }
    MessageStreams::debug() << message::lock
			    << "... done" << std::endl
			    << message::unlock;
  }
  
  // destructor:
  template <class IndexSet, comparison_mode_type mode>
  inline SwitchTable<IndexSet, mode>::~SwitchTable() {}

  // assignment:
  template <class IndexSet, comparison_mode_type mode>
  inline SwitchTable<IndexSet, mode>& SwitchTable<IndexSet, mode>::operator=(const SwitchTable& st) {
    if (&st == this) {
      return *this;
    }
    switch_table_data_type::operator=(st);
    _n = st._n;
    _non_trivial_rowset = st._non_trivial_rowset;
    _non_trivial_colsetvec = st._non_trivial_colsetvec;
    _identity = st._identity;
    _order = st._order;
    return *this;
  }

  // accessors:
  template <class IndexSet, comparison_mode_type mode>
  inline parameter_type SwitchTable<IndexSet, mode>::n() const {
    return _n;
  }

  template <class IndexSet, comparison_mode_type mode>
  inline const IndexSet& SwitchTable<IndexSet, mode>::non_trivial_rowset() const {
    return _non_trivial_rowset;
  }

  template <class IndexSet, comparison_mode_type mode>
  inline const std::vector<IndexSet>& SwitchTable<IndexSet, mode>::non_trivial_colsetvec() const {
    return _non_trivial_colsetvec;
  }

  template <class IndexSet, comparison_mode_type mode>
  inline const Symmetry& SwitchTable<IndexSet, mode>::identity() const {
    return _identity;
  }

  template <class IndexSet, comparison_mode_type mode>
  inline size_type SwitchTable<IndexSet, mode>::order() const {
    return _order;
  }

  template <class IndexSet, comparison_mode_type mode>
  inline size_type SwitchTable<IndexSet, mode>::seecount() const {
    return _seecount;
  }

  // standard functions:
  template <class IndexSet, comparison_mode_type mode>
  bool SwitchTable<IndexSet, mode>::empty() const {
    return _non_trivial_rowset.empty();
  }

  template <class IndexSet, comparison_mode_type mode>
  size_type SwitchTable<IndexSet, mode>::compute_order() const {
    size_type result(1UL);
    for (IndexSet_const_iterator rowiter = _non_trivial_rowset.begin();
	 rowiter != _non_trivial_rowset.end();
	 ++rowiter) {
      result *= (_non_trivial_colsetvec.at(*rowiter).card() + 1);
    }
    return result;
  }

  template <class IndexSet, comparison_mode_type mode>
  bool SwitchTable<IndexSet, mode>::is_in_group(const Symmetry& s) const {
    Symmetry product(s);
    MessageStreams::debug() << message::lock
			    << "initial permutation : " << product << std::endl
			    << message::unlock;
    for (parameter_type i = 0; i < _n; ++i) {
      const parameter_type elem(_elem(i));
      if (product(elem) != elem) {
	for (parameter_type j = _mincol(elem); j < _maxcol(elem); ++j) {
	  if (product(j) == elem) {
	    if (!_non_trivial_colsetvec.at(i).contains(j)) {
	      MessageStreams::debug() << message::lock
				      << "current permutation : " << product << '\n'
				      << "switch table        :\n"
				      << *this << std::endl
				      << message::unlock;
	      return false;
	    }
	    else {
	      MessageStreams::debug() << message::lock
				      << "current permutation : " << product * this->at(i).at(j).inverse()
				      << " = " << product
				      << " * " << this->at(i).at(j).inverse() << std::endl
				      << message::unlock;
	      product = product * this->at(i).at(j).inverse();
	    }
	  }
	}
      }
    }
    MessageStreams::debug() << message::lock
			    << "final permutation   : " << product << std::endl
			    << message::unlock;
    return true;
  }
 
  template <class IndexSet, comparison_mode_type mode>
  bool SwitchTable<IndexSet, mode>::add_to_group(const Symmetry& s) {
    Symmetry product(s);
    MessageStreams::debug() << message::lock
			    << "initial permutation : " << product << std::endl
			    << message::unlock;
    for (parameter_type i = 0; i < _n; ++i) {
      const parameter_type elem(_elem(i));
      if (product(elem) != elem) {
	for (parameter_type j = _mincol(elem); j < _maxcol(elem); ++j) {
	  if (product(j) == elem) {
	    if (!_non_trivial_colsetvec.at(i).contains(j)) {
	      this->_non_trivial_rowset += i;
	      this->_non_trivial_colsetvec.at(i) += j;
	      if (i >= this->size()) {
		this->resize(i + 1, switch_table_row_type(_n));
	      }
	      this->at(i).at(j) = product;
	      return true;
	    }
	    else {
	      product = product * this->at(i).at(j).inverse();
	      MessageStreams::debug() << message::lock
				      << "current permutation : " << product << std::endl
				      << message::unlock;
	    }
	  }
	}
      }
    }
    MessageStreams::debug() << message::lock
			    << "final permutation   : " << product << std::endl
			    << message::unlock;
    return false;
  }

  template <class IndexSet, comparison_mode_type mode>
  size_type SwitchTable<IndexSet, mode>::stabilizer_card(const IntegerSet& is) const {
    return count_stabilizing_switchproducts(0, is, is);
  }
  
  template <class IndexSet, comparison_mode_type mode>
  size_type SwitchTable<IndexSet, mode>::stabilizer_card(const IntegerSet64& is) const {
    return count_stabilizing_switchproducts(0, is, is);
  }

  template <class IndexSet, comparison_mode_type mode>
  size_type SwitchTable<IndexSet, mode>::orbit_size(const IntegerSet& is) const {
    return _order / stabilizer_card(is);
  }

  template <class IndexSet, comparison_mode_type mode>
  size_type SwitchTable<IndexSet, mode>::orbit_size(const IntegerSet64& is) const {
    return _order / stabilizer_card(is);
  }

  template <class IndexSet, comparison_mode_type mode>
  symmetry_collectordata SwitchTable<IndexSet, mode>::all_switches(const parameter_type i) const {
    symmetry_collectordata result;
    for (IndexSet_const_iterator coliter = _non_trivial_colsetvec.at(i).begin();
	 coliter != _non_trivial_colsetvec.at(i).end();
	 ++coliter) {
      result.insert(this->at(i).at(*coliter));
    }
    return result;
  }

  template <class IndexSet, comparison_mode_type mode>
  symmetry_collectordata SwitchTable<IndexSet, mode>::all_switches() const {
    symmetry_collectordata result;
    for (IndexSet_const_iterator rowiter = _non_trivial_rowset.begin();
	 rowiter != _non_trivial_rowset.end();
	 ++rowiter) {
      for (IndexSet_const_iterator coliter = _non_trivial_colsetvec.at(*rowiter).begin();
	   coliter != _non_trivial_colsetvec.at(*rowiter).end();
	   ++coliter) {
	result.insert(this->at(*rowiter).at(*coliter));
      }
    }
    return result;
  }

  template <class IndexSet, comparison_mode_type mode>
  void SwitchTable<IndexSet, mode>::gen_symmetries(const Symmetry& current_product,
						   const parameter_type i,
						   symmetry_iterdata& result) const {
    if (i > _non_trivial_rowset.max_elem()) {
      return;
    }
    if (empty()) {
      return;
    }

    // handle the identity separately:
    gen_symmetries(current_product, i + 1, result);
    for (IndexSet_const_iterator coliter = _non_trivial_colsetvec.at(i).begin();
	 coliter != _non_trivial_colsetvec.at(i).end();
	 ++coliter) {
      const Symmetry new_product = this->at(i).at(*coliter) * current_product;
      MessageStreams::debug() << message::lock
			      << "adding permutation " << new_product << " ..."
			      << std::endl
			      << message::unlock;
      result.push_back(new_product);
      MessageStreams::debug() << message::lock
			      << "... done."
			      << std::endl
			      << message::unlock;
      gen_symmetries(new_product, i + 1, result);
    }
  }
  
  template <class IndexSet, comparison_mode_type mode>
  symmetry_iterdata SwitchTable<IndexSet, mode>::all_symmetries() const {
    symmetry_iterdata result;
    if (empty()) {
      return result;
    }
    gen_symmetries(_identity, 1, result);
    for (IndexSet_const_iterator coliter = _non_trivial_colsetvec.at(0).begin();
	 coliter != _non_trivial_colsetvec.at(0).end();
	 ++coliter) {
      const Symmetry new_product = this->at(0).at(*coliter);
      MessageStreams::debug() << message::lock
			      << "adding permutation " << new_product << " ..."
			      << std::endl
			      << message::unlock;
      result.push_back(new_product);
      MessageStreams::debug() << message::lock
			      << "... done."
			      << std::endl
			      << message::unlock;
      gen_symmetries(new_product, 1, result);
    }
    return result;
  }

  template <class IndexSet, comparison_mode_type mode>
  symmetry_iterdata SwitchTable<IndexSet, mode>::all_symmetries_bfs() const {
    symmetry_iterdata results;
    if (empty()) {
      return results;
    }
    for (IndexSet_const_iterator rowiter = _non_trivial_rowset.begin();
	 rowiter != _non_trivial_rowset.end();
	 ++rowiter) {
      symmetry_iterdata new_results;
      for (IndexSet_const_iterator coliter = _non_trivial_colsetvec.at(*rowiter).begin();
	   coliter != _non_trivial_colsetvec.at(*rowiter).end();
	   ++coliter) {
	const Symmetry switchperm = this->at(*rowiter).at(*coliter);
	MessageStreams::debug() << message::lock
				<< "adding pure switch " << switchperm << " ..."
				<< std::endl
				<< message::unlock;
	new_results.push_back(switchperm);
	MessageStreams::debug() << message::lock
				<< "... done."
				<< std::endl
				<< message::unlock;
	for (symmetry_iterdata::const_iterator resultiter = results.begin();
	     resultiter != results.end();
	     ++resultiter) {	
	  const Symmetry new_product = switchperm * *resultiter;
	  MessageStreams::debug() << message::lock
				  << "adding permutation " << new_product << " ..."
				  << std::endl
				  << message::unlock;
	  new_results.push_back(new_product);
	  MessageStreams::debug() << message::lock
				  << "... done."
				  << std::endl
				  << message::unlock;
	}
      }
      results.insert(results.end(), new_results.begin(), new_results.end());
    }
    return results;
  }
  
  // extract those switches that may lex-decrease/colex-increase a IndexSet:
  template <class IndexSet, comparison_mode_type mode>
  switch_selection_type SwitchTable<IndexSet, mode>::canonicalizing_switchptrs(const parameter_type i,
									       const IndexSet& ls) const {

    // these switches necessarily canonicalize ls strictly:
    switch_selection_type result;
    result.reserve(_non_trivial_colsetvec.at(i).card());
    const parameter_type elem(_elem(i));
    if (_non_trivial_rowset.contains(i)) {
      if (!ls.contains(elem)) {
	const IndexSet canonicalizing_colset = _non_trivial_colsetvec.at(i) * ls;
	for (IndexSet_const_iterator coliter = canonicalizing_colset.begin();
	     coliter != canonicalizing_colset.end();
	     ++coliter) {
	  const parameter_type j(*coliter);
	  result.emplace_back(&this->at(i).at(j));
	}
      }
    }
    return result;
  }

  template <class IndexSet, comparison_mode_type mode>
  switch_selection_type SwitchTable<IndexSet, mode>::neutral_switchptrs(const parameter_type i,
									const IndexSet& ls) const {

    // these switches behave neutrally w.r.t. ls:
    switch_selection_type result;
    result.reserve(_non_trivial_colsetvec.at(i).card());
    const parameter_type elem(_elem(i));
    if (_non_trivial_rowset.contains(i)) {
      for (IndexSet_const_iterator coliter = _non_trivial_colsetvec.at(i).begin();
	   coliter != _non_trivial_colsetvec.at(i).end();
	   ++coliter) {
	const parameter_type j(*coliter);
	if ((ls.contains(elem) && ls.contains(j))
	    || (!ls.contains(elem) && !ls.contains(j))) {
	  result.emplace_back(&this->at(i).at(j));
	}
      }
    }
    return result;
  }

  template <class IndexSet, comparison_mode_type mode>
  bool SwitchTable<IndexSet, mode>::not_canonical(const parameter_type i,
						  const IndexSet&      ls_org,
						  const IndexSet&      ls_img) const {
    if (empty()) {
      return false;
    }
    if (i > _non_trivial_rowset.max_elem()) {
      return false;
    }

    // handle the identity individually without bogus mapping:
    if (not_canonical(i + 1, ls_org, ls_img)) {
      return true;
    }
    switch_selection_type canonicalizers(canonicalizing_switchptrs(i, ls_img));
    if (canonicalizers.empty()) {
      switch_selection_type neutrals(neutral_switchptrs(i, ls_img));
      for (switch_selection_type::const_iterator iter = neutrals.begin();
	   iter != neutrals.end();
	   ++iter) {
	const IndexSet new_ls_img(ls_img.permute(**iter));
	if (_compare_subsets(new_ls_img, ls_org)) {
	  return true;
	}
	if (not_canonical(i + 1, ls_org, new_ls_img)) {
	  return true;
	}
      }
    }
    else {
      for (switch_selection_type::const_iterator iter = canonicalizers.begin();
	   iter != canonicalizers.end();
	   ++iter) {
	const IndexSet new_ls_img(ls_img.permute(**iter));
	if (_compare_subsets(new_ls_img, ls_org)) {
	  return true;
	}
	if (not_canonical(i + 1, ls_org, new_ls_img)) {
	  return true;
	}
      }
    }
    return false;
  }

  template <class IndexSet, comparison_mode_type mode>
  inline bool SwitchTable<IndexSet, mode>::not_canonical(const IndexSet& ls) const {
    return not_canonical(0, ls, ls);
  }


  template <class IndexSet, comparison_mode_type mode>
  bool SwitchTable<IndexSet, mode>::not_canonicalPR(const parameter_type card,
						    const parameter_type i,
						    const IndexSet& ls_org,
						    const IndexSet& ls_img) const {

    // a combination of the Pech-Reichard-algorithm with the power of switch tables:
    // since this method is tight, we need to guard away all kinds of debug output:
#ifdef ST_DEBUG
    MessageStreams::debug() << message::lock
			    << "does G[" << i << "] map " << ls_img
			    << " to a better set than original set " << ls_org << "?" << std::endl
			    << message::unlock;
#endif

    if (card == 0) {
#ifdef ST_DEBUG
      MessageStreams::debug() << message::lock
			      << "original set " << ls_org << " empty - false" << std::endl
			      << message::unlock;
#endif
      return false;
    }
    else if (card >= _n - i) {
#ifdef ST_DEBUG
      MessageStreams::debug() << message::lock
			      << "complement of original set " << ls_org << " empty - false" << std::endl
			      << message::unlock;
#endif
      return false;
    }
    else {
#ifdef ST_DEBUG
      MessageStreams::debug() << message::lock
			      << "original set " << ls_org << " and complement non-empty - process" << std::endl
			      << message::unlock;
#endif
    }

    if (empty()) {
#ifdef ST_DEBUG
      MessageStreams::debug() << message::lock
			      << "group is trivial - compare argument sets" << std::endl
			      << message::unlock;
#endif
      _seecount += card;
      return _compare_subsets(ls_img, ls_org);
    }

    if (i > _non_trivial_rowset.max_elem()) {
#ifdef ST_DEBUG
      MessageStreams::debug() << message::lock
			      << "group traversal at level " << i
			      << " is trivial - compare argument sets" << std::endl
			      << message::unlock;
#endif
      _seecount += card;
      return _compare_subsets(ls_img, ls_org);
    }

    // the following can be tried to short-cut
    // (experiments show no benefit, though):
    // if (_compare_subsets(ls_img, ls_org)) {
    //   _seecount += card;
    //   return true;
    // }
    
    const parameter_type elem(_elem(i));
    if (ls_org.contains(elem)) {
      const IndexSet new_ls_img(ls_img - elem);
      IndexSet new_ls_org(ls_org - elem);
      if (ls_img.contains(elem)) {
#ifdef ST_DEBUG
	MessageStreams::debug() << message::lock
				<< "original set " << ls_org << " contains " << elem << '\n'
				<< "mapped set   " << ls_img << " contains " << elem << '\n'
				<< "recurse:" << std::endl
				<< message::unlock;
#endif
	
	// only in this case the identity switch suffices as a factor:
	if (not_canonicalPR(card - 1, i + 1, new_ls_org, new_ls_img)) {
#ifdef ST_DEBUG
	  MessageStreams::debug() << message::lock
				  << "G[" << i + 1 << "] maps " << new_ls_img
				  << " to a better set than " << new_ls_org << " - true" << std::endl
				  << message::unlock;
#endif
	  return true;
	}
      }
      
      // now process all neutral non-identity switches mapping elements to elements:
      for (IndexSet_const_iterator preimg_iter = _non_trivial_colsetvec.at(i).begin();
	   preimg_iter != _non_trivial_colsetvec.at(i).end();
	   ++preimg_iter) {
	const parameter_type j(*preimg_iter);
	if (!ls_img.contains(j)) {
	  
	  // we first have to make sure that ls_img hits
	  // the current extremal element elem of ls_org:
	  continue;
	}
	const IndexSet new_ls_img((ls_img - j).permute(this->at(i).at(j)));
	_seecount += card - 1;
	if (not_canonicalPR(card - 1, i + 1, new_ls_org, new_ls_img)) {
#ifdef ST_DEBUG
	  MessageStreams::debug() << message::lock
				  << "original set " << ls_org << " contains " << elem << '\n'
				  << "mapped set   " << ls_img << " contains " << elem << '\n'
				  << "G[" << i + 1 << "] maps " << new_ls_img
				  << " to a better set than " << new_ls_org << " - true" << std::endl
				  << message::unlock;
#endif
	  return true;
	}
      }
#ifdef ST_DEBUG
      MessageStreams::debug() << message::lock
			      << "original set " << ls_org << " contains " << elem << '\n'
			      << "mapped set   " << ls_img << " contains " << elem << '\n'
			      << "G[" << i + 1 << "] does not map " << new_ls_img
			      << " to a better set than " << new_ls_org << " - false" << std::endl
			      << message::unlock;
#endif
      return false;
    }
    else {
      if (ls_img.contains(elem)) {
#ifdef ST_DEBUG
	MessageStreams::debug() << message::lock
				<< "original set " << ls_org << " does not contain " << elem << '\n'
				<< "mapped set   " << ls_img << " contains         " << elem << '\n'
				<< "identity maps set to a better set than original set - true" << std::endl
				<< message::unlock;
#endif
	return true;
      }
      else if (_non_trivial_rowset.contains(i)
	       && _non_trivial_colsetvec.at(i).intersection_nonempty(ls_img)) {
#ifdef ST_DEBUG
	MessageStreams::debug() << message::lock
				<< "original set " << ls_org << " does not contain " << elem << '\n'
				<< "mapped set   " << ls_img << " does not contain " << elem << '\n'
				<< "exist switches with j in " << _non_trivial_colsetvec.at(i) * ls_img
				<< " mapping elements of mapped set " << ls_img
				<< " to " << elem << " " << '\n'
				<< "G[" << i << "] maps mapped set to a better set than original set - true" << std::endl
				<< message::unlock;
#endif
	return true;  
      }
      else {
#ifdef ST_DEBUG
	MessageStreams::debug() << message::lock
				<< "original set " << ls_org << " does not contain " << elem << '\n'
				<< "mapped set   " << ls_img << " does not contain " << elem << '\n'
				<< "recurse:" << std::endl
				<< message::unlock;
#endif
	
	// start implicitly with the identity switch:
	if (not_canonicalPR(card, i + 1, ls_org, ls_img)) {
#ifdef ST_DEBUG
	  MessageStreams::debug() << message::lock
				  << "G[" << i + 1 << "] maps " << ls_img
				  << " to a better set than " << ls_org << " - true" << std::endl
				  << message::unlock;
#endif
	  return true;
	}

	// now process all neutral non-identity switches mapping non-elements to a non-element:
	for (IndexSet_const_iterator preimg_iter = _non_trivial_colsetvec.at(i).begin();
	     preimg_iter != _non_trivial_colsetvec.at(i).end();
	     ++preimg_iter) {
	  const parameter_type j(*preimg_iter);

	  // since the intersection of the effective column set and ls_img is empty,
	  // the following cannot happen anyway:
	  // if (ls_img.contains(j)) {
	  //   continue;
	  // }
	  const IndexSet new_ls_img(ls_img.permute(this->at(i).at(j)));
	  _seecount += card;
	  if (not_canonicalPR(card, i + 1, ls_org, new_ls_img)) {
#ifdef ST_DEBUG
	    MessageStreams::debug() << message::lock
				    << "original set " << ls_org << " does not contain " << elem << '\n'
				    << "mapped set   " << ls_img << " does not contain " << elem << '\n'
				    << "G[" << i + 1 << "] maps " << new_ls_img
				    << " to a better set than " << ls_org << " - true" << std::endl
				    << message::unlock;
#endif	    
	    return true;
	  }
	}
#ifdef ST_DEBUG
	MessageStreams::debug() << message::lock
				<< "original set " << ls_org << " does not contain " << elem << '\n'
				<< "mapped set   " << ls_img << " does not contain " << elem << '\n'
				<< "G[" << i + 1 << "] does not map " << ls_img
				<< " to a better set than " << ls_org << " - false" << std::endl
				<< message::unlock;
#endif
	return false;
      }
    }
  }  

  template <class IndexSet, comparison_mode_type mode>
  inline bool SwitchTable<IndexSet, mode>::not_canonicalPR(const IndexSet& ls) const {
    return not_canonicalPR(ls.card(), 0, ls, ls);
  }

  // extract those switches that may stabilize a IndexSet:
  template <class IndexSet, comparison_mode_type mode>
  switch_selection_type SwitchTable<IndexSet, mode>::stabilizing_switchptrs(const parameter_type i,
									    const IndexSet&      ls_org,
									    const IndexSet&      ls_img) const {
    switch_selection_type result;
    result.reserve(_non_trivial_colsetvec.at(i).card());
    const parameter_type elem(_elem(i));
    if (_non_trivial_rowset.contains(i)) {
      for (IndexSet_const_iterator coliter = _non_trivial_colsetvec.at(i).begin();
	   coliter != _non_trivial_colsetvec.at(i).end();
	   ++coliter) {
	const parameter_type j(*coliter);
	if (ls_org.contains(elem) && !ls_img.contains(j)) {

	  // future switches can never push elem into ls_img again,
	  // so ls_img cannot be mapped to ls_org containing elem:
	  continue;
	}
	if (!ls_org.contains(elem) && ls_img.contains(j)) {

	  // future switches can never push elem out of ls_img again,
	  // so ls_img cannot be mapped to ls_org not containing elem:
	  continue;
	}
	result.emplace_back(&this->at(i).at(j));
      }
    }
    return result;
  }

  template <class IndexSet, comparison_mode_type mode>
  size_type SwitchTable<IndexSet, mode>::count_stabilizing_switchproducts(const parameter_type i,
									  const IndexSet&      ls_org,
									  const IndexSet&      ls_img) const {
    if (empty()) {

      // count the identity:
      return 1UL;
    }
    if (i > _non_trivial_rowset.max_elem()) {

      // count the identity:
      return 1UL;
    }

    // count the identity first:
    size_type result = 1UL;    
    const parameter_type elem(_elem(i));

    // handle the identity individually without bogus mapping (the identity alone was counted already):
    if (!ls_org.contains(elem) && !ls_img.contains(elem)) {

      // elem is already missing in both ls_img and ls_org, which cannot be changed
      // by the remaining switches, since they fix elem:
      result += count_stabilizing_switchproducts(i + 1, ls_org, ls_img) - 1;
    }
    const IndexSet new_ls_org(ls_org - elem);
    IndexSet new_ls_img(ls_img - elem);
    if (ls_org.contains(elem) && ls_img.contains(elem)) { 

      // elem is already in both ls_img and ls_org, which cannot be changed
      // by the remaining switches, since they fix elem:
      result += count_stabilizing_switchproducts(i + 1, new_ls_org, new_ls_img) - 1;
    }

    if (_non_trivial_rowset.contains(i)) {
      for (IndexSet_const_iterator iter = _non_trivial_colsetvec.at(i).begin();
	   iter != _non_trivial_colsetvec.at(i).end();
	   ++iter) {
	const parameter_type j(*iter);
	if (ls_org.contains(elem) && !ls_img.contains(j)) {

	  // future switches can never push elem into ls_img again,
	  // so ls_img cannot be mapped using this switch
	  // to ls_org containing elem:
	  continue;
	}
	if (!ls_org.contains(elem) && ls_img.contains(j)) {

	  // future switches can never push elem out of ls_img again,
	  // so ls_img cannot be mapped using this switch
	  // to ls_org not containing elem:
	  continue;
	}
	if (this->at(i).at(j).maps(ls_img, ls_org)) {
	  
	  // found a stabilizing switch product:
	  ++result;
	  
	  // recursively extend the product, where we are already back at ls_org:
	  result += count_stabilizing_switchproducts(i + 1, new_ls_org, new_ls_org) - 1;
	}
	else {
	  
	  // map ls_img - j to new_ls_img:
	  new_ls_img = (ls_img - j).permute(this->at(i).at(j));
	  
	  // recursively extend the product (the identity was counted already):
	  result += count_stabilizing_switchproducts(i + 1, new_ls_org, new_ls_img) - 1;
	}
      }
    }
    return result;
  }

  template <class IndexSet, comparison_mode_type mode>
  bool SwitchTable<IndexSet, mode>::lex_decreases(const IndexSet& ls) const {
    const bool result(not_canonicalPR(ls.card(), 0, ls, ls));
    if (mode != lexmin_mode) {
      MessageStreams::forced() << message::lock
			       << "SwitchTable::lex_decreases(...): wrong mode - exiting." << std::endl
			       << message::unlock;
      exit(1);
    }
    if (CommandlineOptions::debug()) {
      if (result != not_canonical(0, ls, ls)) {
	MessageStreams::forced() << message::lock
				 << "SwitchTable<IndexSet, mode>::lex_decreases(const IndexSet& ls) const: "
				 << "incompatible results of not_canonicalPR and not_canonical - exiting." << '\n'
				 << "switch table:\n" << *this << std::endl
				 << message::unlock;
	exit(1);
      }
    }
    return result;
  }

  template <class IndexSet, comparison_mode_type mode>
  bool SwitchTable<IndexSet, mode>::colex_increases(const IndexSet& ls) const {
    const bool result(not_canonicalPR(ls.card(), 0, ls, ls));
    if (mode != colexmax_mode) {
      MessageStreams::forced() << message::lock
			       << "SwitchTable::colex_increases(...): wrong mode - exiting." << std::endl
			       << message::unlock;
      exit(1);
    }
    if (CommandlineOptions::debug()) {
      if (result != not_canonical(0, ls, ls)) {
	MessageStreams::forced() << message::lock
				 << "SwitchTable<IndexSet, mode>::colex_increases(const IndexSet& ls) const: "
				 << "incompatible results of not_canonicalPR and not_canonical - exiting." << '\n'
				 << "switch table:\n" << *this << std::endl
				 << message::unlock;
	exit(1);
      }
    }
    return result;
  }
  
  // stream input/output:
  template <class IndexSet, comparison_mode_type mode>
  inline std::istream& SwitchTable<IndexSet, mode>::read(std::istream& ist) {
    char c;

    MessageStreams::debug() << message::lock
			    << "clearing old data:\n"
			    << message::unlock;
    clear();
    for (IndexSet_const_iterator rowiter = _non_trivial_rowset.begin();
	 rowiter != _non_trivial_rowset.end();
	 ++rowiter) {
      _non_trivial_colsetvec.at(*rowiter).clear();
    }
    _non_trivial_rowset.clear();
    
    MessageStreams::debug() << message::lock
			    << "reading degree:\n"
			    << message::unlock;
    MessageStreams::debug() << message::lock
			    << "read " << _n << "\n"
			    << message::unlock;
    if (!(ist >> std::ws >> _n >> std::ws >> c >> std::ws)) {
      MessageStreams::forced() << message::lock
			       << "SwitchTable<IndexSet, mode>::read(std::istream&): degree not found - exiting" << '\n'
			       << message::unlock;
      exit(1);
    };
    _identity = Symmetry(_n);
    MessageStreams::debug() << message::lock
			    << "reading dense data:\n"
			    << message::unlock;
    ist >> static_cast<switch_table_data_type&>(*this);
    MessageStreams::debug() << message::lock
      <<  "read " << static_cast<const switch_table_data_type&>(*this)
			    << '\n'
			    << message::unlock;
    MessageStreams::debug() << message::lock
			    << "generating non-trivial row and column sets:"
			    << message::unlock;
    _non_trivial_colsetvec.resize(_n);
    for (parameter_type row = 0; row < this->size(); ++row) {
      if (!this->at(row).empty()) {
	_non_trivial_rowset += row;
	for (parameter_type col = 0; col < this->at(row).size(); ++col) {
	  if (this->at(row).at(col) != Symmetry(0)) {
	    _non_trivial_colsetvec.at(row) += col;
	  }
	}
      }
    }
    _order = compute_order();
    MessageStreams::debug() << message::lock
			    << "returning input stream:"
			    << message::unlock;
    return ist;
  }

  template <class IndexSet, comparison_mode_type mode>
  inline std::istream& SwitchTable<IndexSet, mode>::sparse_read(std::istream& ist) {
    char dash;
    char arrow;
    char c;
  
    parameter_type row_reader;
    parameter_type col_reader;
    Symmetry       sym_reader;

    // get rid of possible old data:
    switch_table_data_type::clear();
    for (parameter_type row = 0; row < _n; ++row) {
      _non_trivial_colsetvec.at(row).clear();
    }
    _non_trivial_rowset.clear();

    // get new degree:
    if (!(ist >> std::ws >> _n >> std::ws >> c >> std::ws)
	|| (c != ContainerChars::colon)) {
      MessageStreams::forced() << message::lock
			       << "SwitchTable<IndexSet, mode>::sparse_read(std::istream&): degree not found - exiting" << '\n'
			       << message::unlock;
      exit(1);
    }

    // derive data only dependent on degree:
    this->resize(_n, switch_table_row_type(_n));
    _non_trivial_colsetvec.resize(_n);
    _identity = Symmetry(_n);

    // read sparse data:
    ist >> std::ws >> c;
    if (c == ContainerChars::set_start_char) {
      
      // start of switch-table data:
      while (ist >> std::ws >> c) {
	if (c == ContainerChars::set_end_char) {
	  
	  // end of switch-table row data:
	  break;
	}
	if (c == ContainerChars::delim_char) {
	  
	  // next switch-table row:
	  continue;
	}
	if (c == ContainerChars::set_start_char) {
	  
	  // data of one row starts here with row index:
	  if (!(ist >> std::ws >> row_reader >> c)
	      || (c != ContainerChars::colon)) {
	    
	    // row index and/or colon missing:
	    ist.clear(std::ios::failbit);
	    return ist;
	  }
	  _non_trivial_rowset += row_reader;
	  
	  // start of switch-table column data for row index:
	  while (ist >> std::ws >> c) {
	    if (c == ContainerChars::set_end_char) {
	      
	      // end of switch-table column data:
	      break;
	    }
	    if (c == ContainerChars::delim_char) {
	      
	      // next switch-table column:
	      continue;
	    }
	    ist.putback(c);

	    // data of one column starts here with column index:
	    if (!(ist >> std::ws >> col_reader)) {
	      
	      // colum index missing:
	      ist.clear(std::ios::failbit);
	      return ist;
	    }
	    if (!(ist >> std::ws >> dash >> arrow)
		|| (dash != ContainerChars::dash)
		|| (arrow != ContainerChars::arrow)) {
	      
	      // "->" missing:
	      ist.clear(std::ios::failbit);
	      return ist;
	    }
	    if (!(ist >> std::ws >> sym_reader)) {
	      
	      // symmetry missing
	      ist.clear(std::ios::failbit);
	      return ist;
	    }
	    _non_trivial_colsetvec.at(row_reader) += col_reader;
	    this->at(row_reader).at(col_reader) = sym_reader;
	  }
	}
	else {
	  ist.clear(std::ios::failbit);
	  return ist;
	}
      }
    }
    else {
      ist.clear(std::ios::failbit);
      return ist;
    }
    ist.clear(std::ios::goodbit);
    _order = compute_order();
    
    return ist;
  }
  
  template <class StreamIndexSet, comparison_mode_type streammode>
  inline std::istream& operator>>(std::istream& ist, SwitchTable<StreamIndexSet, streammode>& st) {
    return st.read(ist);
  }
  
  template <class IndexSet, comparison_mode_type mode>
  inline Message& SwitchTable<IndexSet, mode>::write(Message& ost) const {
    ost << _n << ":";
    ost << static_cast<const switch_table_data_type&>(*this);
    return ost;
  }
  
  template <class IndexSet, comparison_mode_type mode>
  inline std::ostream& SwitchTable<IndexSet, mode>::write(std::ostream& ost) const {
    Message msg(ost);
    write(msg);
    return ost;
  }

  template <class IndexSet, comparison_mode_type mode>
  inline Message& SwitchTable<IndexSet, mode>::sparse_write(Message& ost) const {
    ost << _n << ContainerChars::colon << ContainerChars::set_start_char;
    for (IndexSet_const_iterator rowiter = _non_trivial_rowset.begin();
	 rowiter != _non_trivial_rowset.end();
	 ++rowiter) {
      ost << ContainerChars::set_start_char;
      ost << *rowiter << ":";
      if (!_non_trivial_colsetvec.at(*rowiter).empty()) {
	IndexSet_const_iterator coliter = _non_trivial_colsetvec.at(*rowiter).begin();
	ost << *coliter << ContainerChars::map_chars << this->at(*rowiter).at(*coliter);
	while (++coliter != _non_trivial_colsetvec.at(*rowiter).end()) {
	  ost << ContainerChars::delim_char << *coliter << ContainerChars::map_chars << this->at(*rowiter).at(*coliter);
	}
      }
      ost << ContainerChars::set_end_char;
    }
    ost << ContainerChars::set_end_char;
    return ost;
  }

  template <class IndexSet, comparison_mode_type mode>
  inline std::ostream& SwitchTable<IndexSet, mode>::sparse_write(std::ostream& ost) const {
    Message msg(ost);
    sparse_write(msg);
    return ost;
  }

  template <class IndexSet, comparison_mode_type mode>
  inline Message& SwitchTable<IndexSet, mode>::write_sorted_generators(Message& ost) const {
    std::set<Symmetry> sorted_generators;
    symmetry_collectordata symcollector(this->all_switches());
    std::copy(symcollector.begin(), symcollector.end(), std::inserter(sorted_generators, sorted_generators.begin()));
    std::set<Symmetry>::const_iterator iter = sorted_generators.begin();
    ost << ContainerChars::list_start_char;
    ost << *iter;
    while (++iter != sorted_generators.end()) {
      ost << ContainerChars::delim_char << *iter;
    }
    ost << ContainerChars::list_end_char;
    return ost;
  }

  template <class IndexSet, comparison_mode_type mode>
  inline std::ostream& SwitchTable<IndexSet, mode>::write_sorted_generators(std::ostream& ost) const {
    Message msg(ost);
    write_sorted_generators(msg);
    return ost;
  }

  template <class IndexSet, comparison_mode_type mode>
  inline Message& SwitchTable<IndexSet, mode>::pretty_print(Message& ost) const {
    ost << "switch table:\n";
    for (IndexSet_const_iterator rowiter = _non_trivial_rowset.begin();
	 rowiter != _non_trivial_rowset.end();
	 ++rowiter) {
      ost << "row " << *rowiter << ":";
      for (IndexSet_const_iterator coliter = _non_trivial_colsetvec.at(*rowiter).begin();
	   coliter != _non_trivial_colsetvec.at(*rowiter).end();
	   ++coliter) {
	ost << " " << *coliter << ContainerChars::map_chars << this->at(*rowiter).at(*coliter);
      }
      ost << '\n';
    }
    return ost;
  }

  template <class IndexSet, comparison_mode_type mode>
  inline std::ostream& SwitchTable<IndexSet, mode>::pretty_print(std::ostream& ost) const {
    Message msg(ost);
    pretty_print(msg);
    return ost;
  }

  
  template <class StreamIndexSet, comparison_mode_type streammode>
  inline std::ostream& operator<<(std::ostream& ost, const SwitchTable<StreamIndexSet, streammode>& st) {
    return st.write(ost);
  }
  
  // internal methods:
  template <>
  inline parameter_type SwitchTable<IntegerSet, lexmin_mode>::_elem(const parameter_type i) const {
    return i;
  }

  template <>
  inline parameter_type SwitchTable<IntegerSet, colexmax_mode>::_elem(const parameter_type i) const {
    return _n - i - 1;
  }

  template <>
  inline parameter_type SwitchTable<IntegerSet, lexmin_mode>::_mincol(const parameter_type elem) const {
    return elem + 1;
  }

  template <>
  inline parameter_type SwitchTable<IntegerSet, colexmax_mode>::_mincol(const parameter_type elem) const {
    return 0;
  }

  template <>
  inline parameter_type SwitchTable<IntegerSet, lexmin_mode>::_maxcol(const parameter_type elem) const {
    return _n;
  }

  template <>
  inline parameter_type SwitchTable<IntegerSet, colexmax_mode>::_maxcol(const parameter_type elem) const {
    return elem;
  }

  template <>
  inline bool SwitchTable<IntegerSet, lexmin_mode>::_compare_subsets(const IntegerSet& ls1,
								     const IntegerSet& ls2) const {
    return ls1.lexsmaller(ls2);
  }

  template <>
  inline bool SwitchTable<IntegerSet, colexmax_mode>::_compare_subsets(const IntegerSet& ls1,
								       const IntegerSet& ls2) const {
    return ls1.colexgreater(ls2);
  }

  template <>
  inline parameter_type SwitchTable<IntegerSet64, lexmin_mode>::_elem(const parameter_type i) const {
    return i;
  }

  template <>
  inline parameter_type SwitchTable<IntegerSet64, colexmax_mode>::_elem(const parameter_type i) const {
    return _n - i - 1;
  }

  template <>
  inline parameter_type SwitchTable<IntegerSet64, lexmin_mode>::_mincol(const parameter_type elem) const {
    return elem + 1;
  }

  template <>
  inline parameter_type SwitchTable<IntegerSet64, colexmax_mode>::_mincol(const parameter_type elem) const {
    return 0;
  }

  template <>
  inline parameter_type SwitchTable<IntegerSet64, lexmin_mode>::_maxcol(const parameter_type elem) const {
    return _n;
  }

  template <>
  inline parameter_type SwitchTable<IntegerSet64, colexmax_mode>::_maxcol(const parameter_type elem) const {
    return elem;
  }

  template <>
  inline bool SwitchTable<IntegerSet64, lexmin_mode>::_compare_subsets(const IntegerSet64& ls1,
								       const IntegerSet64& ls2) const {
    return ls1.lexsmaller(ls2);
  }

  template <>
  inline bool SwitchTable<IntegerSet64, colexmax_mode>::_compare_subsets(const IntegerSet64& ls1,
									 const IntegerSet64& ls2) const {
    return ls1.colexgreater(ls2);
  }

  template <class IndexSet, comparison_mode_type mode>
  bool SwitchTable<IndexSet, mode>::_add_symmetry(const Symmetry& sym) {
    for (parameter_type i = 0; i < _n; ++i) {
      const parameter_type elem = _elem(i);
      if (sym(elem) != elem) {
	
	// we are in row elem of the switch table,
	// and the column is the preimage of elem:
	for (parameter_type j = _mincol(elem); j < _maxcol(elem); ++j) {
	  if ((sym(j) == elem)
	      && !_non_trivial_colsetvec.at(i).contains(j)) {
	    MessageStreams::debug() << message::lock
				    << "adding switch " << sym << " to cell (" << i << "," << j << ") of switch table" << std::endl
				    << message::unlock;
	    if (i >= this->size()) {
	      this->resize(i + 1, switch_table_row_type(_n));
	    }
	    this->at(i).at(j) = sym;
	    _non_trivial_rowset += i;
	    _non_trivial_colsetvec.at(i) += j;
	    return true;
	  }
	}
	return false;
      }
    }
    return false;
  }

}; // namespace topcom

#endif

// eof SwitchTable.hh
