////////////////////////////////////////////////////////////////////////////////
// 
// SymmetricExtensionGraph.cc
//
//    produced: 12/01/2020 jr
// 
////////////////////////////////////////////////////////////////////////////////

#include <sstream>
#include <iomanip>
#include <thread>
#include <functional>
#include <atomic>

#include "SymmetricExtensionGraph.hh"

namespace topcom {
  std::mutex SymmetricExtensionGraph::_min_mutex;
  
  SymmetricExtensionGraph::SymmetryWorker::SymmetryWorker(const int                         workerID,
							  const SymmetricExtensionGraph&    seg) :
    _workerID(workerID),
    _callerptr(&seg),
    _partial_triang_to_check(),
    _partial_triang_new_simplex(),
    _work_received(false),
    _work_done(true),
    _worker_stopped(false) {
  }

  SymmetricExtensionGraph::SymmetryWorker::SymmetryWorker(const SymmetryWorker& sw) :
    _workerID(sw._workerID),
    _callerptr(sw._callerptr),
    _partial_triang_to_check(sw._partial_triang_to_check),
    _partial_triang_new_simplex(sw._partial_triang_new_simplex),
    _work_received(false),
    _work_done(true),
    _worker_stopped(false) {
  }

  void SymmetricExtensionGraph::SymmetryWorker::operator()() {
    MessageStreams::debug() << message::lock
			    << message::tab
			    << "worker " << _workerID << " is spawned" << std::endl
			    << message::unlock;
    while (!_worker_stopped) {
      MessageStreams::debug() << message::lock
			      << message::tab
			      << "worker " << _workerID << " waiting ..." << std::endl
			      << message::unlock;
      while (!wake_up()) {
	std::unique_lock<std::mutex> main_lock(_callerptr->_main_mutex);
	_worker_condition.wait(main_lock, [this] { return wake_up(); });
      }

      // wake-up because of new work?
      if (_work_received && !_worker_stopped) {
	MessageStreams::debug() << message::lock
				<< message::tab
				<< "worker " << _workerID << " working ..." << std::endl
				<< message::unlock;

	// the actual work is done here:
	check_lex_decreasing_symmetry();
	// actual work done.
    
	MessageStreams::debug() << message::lock
				<< message::tab
				<< "... worker " << _workerID << " done - notifying master" << std::endl
				<< message::unlock;
	{
	  _work_done = true;
	  _work_received = false;
	  std::lock_guard<std::mutex> main_lock(_callerptr->_main_mutex);
	  --_callerptr->_no_of_busy_threads;
	  if (_callerptr->_threads_done()) {
	    _callerptr->_main_condition.notify_one();
	  }
	}
      }
    }
  }

  void SymmetricExtensionGraph::SymmetryWorker::check_lex_decreasing_symmetry() {

    // some other thread may have found that current_partial_triang is not new:
    if (_callerptr->_is_old_symmetry_class) {
      return;
    }
    if (_callerptr->_classified_symmetriesptr->worker_symmetryptrs(_workerID).empty()) {
      return;
    }
    const Simplex& min_simp(*_partial_triang_to_check.begin());
    const seg_symmetryptr_iterdata& relevant_symmetryptrs(_callerptr->_classified_symmetriesptr->worker_relevant_symmetryptrs(_workerID, _partial_triang_new_simplex));
    for (seg_symmetryptr_iterdata::const_iterator iter = relevant_symmetryptrs.begin();
	 iter != relevant_symmetryptrs.end();
	 ++iter) {
      // some other thread may have found that current_partial_triang is not new:
      if (_callerptr->_is_old_symmetry_class) {
	return;
      }
      const Symmetry& g(**iter);
      
      // if (g.map(_partial_triang_new_simplex).lexsmaller(min_simp)) {
      // 	// now this thread has found that current_partial_triang is not new:
      // 	_callerptr->_is_old_symmetry_class = true;
      // 	return;
      // }

      const size_type min_simp_idx = SimplicialComplex::index_of_simplex(min_simp, _callerptr->_rank);
      const size_type g_new_simp_idx = SimplicialComplex::index_of_simplex(g.map(_partial_triang_new_simplex), _callerptr->_rank);
      if (g_new_simp_idx < min_simp_idx) {
	_callerptr->_is_old_symmetry_class = true;
	return;
      }
      
      if (g.lex_decreases(_partial_triang_to_check, _callerptr->_rank)) {
	// now this thread has found that current_partial_triang is not new:
	_callerptr->_is_old_symmetry_class = true;
	return;
      }
    }
  }

  bool SymmetricExtensionGraph::_old_symmetry_class(const PartialTriang& current_partial_triang,
						    const Simplex&       new_simp) {
    if (CommandlineOptions::parallel_symmetries()) {
      {
	std::lock_guard<std::mutex> main_lock(_main_mutex);
	_is_old_symmetry_class = false;
	_no_of_busy_threads = 0;
      }
      for (int i = 0; i < _no_of_threads; ++i) {
	MessageStreams::debug() << message::lock
				<< message::tab
				<< "activating thread " << i << " ..." << std::endl
				<< message::unlock;
	_symmetry_workers[i].pass_work(current_partial_triang, new_simp);
	MessageStreams::debug() << message::lock
				<< message::tab
				<< "... done" << std::endl
				<< message::unlock;
      }
      MessageStreams::debug() << message::lock
			      << "master is notifying all workers ..." << std::endl
			      << message::unlock;
      SimplicialComplex::start_multithreading();
      for (int i = 0; i < _no_of_threads; ++i) {
	_symmetry_workers[i]._worker_condition.notify_one();
	std::lock_guard<std::mutex> main_lock(_main_mutex);
	++_no_of_busy_threads;
      }
      MessageStreams::debug() << message::lock
			      << "master is locking main mutex to wait for threads to finish ..." << std::endl
			      << message::unlock;
      while (!_threads_done()) {
	std::unique_lock<std::mutex> main_lock(_main_mutex);
	_main_condition.wait(main_lock, [this] { return _threads_done(); } );
      }

      SimplicialComplex::stop_multithreading();
      MessageStreams::debug() << message::lock
			      << "workers final result = " << _is_old_symmetry_class << std::endl
			      << message::unlock;
      return _is_old_symmetry_class;
    }
    else {
      if (CommandlineOptions::use_switch_tables()) {
      	return _switch_tableptr->lex_decreases(current_partial_triang.index_set_pure());
      }
      else if (CommandlineOptions::use_classified_symmetries()) {
	return _classified_symmetriesptr->lex_decreases_by_relevant_symmetries(current_partial_triang, new_simp);
      }
      else {

	// in this case, we use the naive traversal through all symmetries:
	if (_simpidx_symmetriesptr) {
	  for (SymmetryGroup::const_iterator iter = _simpidx_symmetriesptr->begin();
	       iter != _simpidx_symmetriesptr->end();
	       ++iter) {
	    if (iter->lex_decreases(current_partial_triang.index_set_pure())) {
	      return true;
	    }
	  }
	  return false;	
	}
	else {
	  for (SymmetryGroup::const_iterator iter = _symmetriesptr->begin();
	       iter != _symmetriesptr->end();
	       ++iter) {
	    if (iter->lex_decreases(current_partial_triang)) {
	      return true;
	    }
	  }
	  return false;	
	}
      }
    }
  }

  bool SymmetricExtensionGraph::_update_coversimps(const PartialTriang&            current_partial_triang,
						   const Simplex&                  freefacet,
						   seg_coversimps_type&            coversimps,
						   seg_coveradmissibles_type&      coveradmissibles,
						   seg_coversimps_flag_type&       these_coversimps_changed,
						   seg_coveradmissibles_flag_type& these_coveradmissibles_changed,
						   bool&                           no_extension_possible) const {
    MessageStreams::debug() << message::lock
			    << message::tab
			    << "updating coversimps of freefacet "
			    << freefacet << " ..." << std::endl
			    << message::unlock;
    SimplicialComplex common_otherfacets_coveradmissibles(current_partial_triang.admissibles());
    SimplicialComplex otherfreefacet_conflictfree; // default construction before loop saves destructions in each loop
    for (SimplicialComplex::const_iterator offiter = current_partial_triang.freeintfacets().begin();
	 offiter != current_partial_triang.freeintfacets().end();
	 ++offiter) {
      if (*offiter == freefacet) {
	continue;
      }
      const Simplex& other_freefacet(*offiter);
      otherfreefacet_conflictfree = coveradmissibles[other_freefacet]; // assignment instead of construction in loop saves destruction
      otherfreefacet_conflictfree += coversimps[other_freefacet];
      common_otherfacets_coveradmissibles *= otherfreefacet_conflictfree;
      // common_otherfacets_coveradmissibles *= (coveradmissibles[other_freefacet] + coversimps[other_freefacet]);
      if (common_otherfacets_coveradmissibles.empty()) {
	MessageStreams::debug() << message::lock
				<< "no common coveradmissibles of other free interior facets: no extension possible." << std::endl
				<< message::unlock;
	if (_iter_coversimptighten > _maxiter_coversimptighten) {
	  _maxiter_coversimptighten = _iter_coversimptighten;
	}
	no_extension_possible = true;
	return true;
      }
    }
    MessageStreams::debug() << message::lock
			      << "intersection of coveradmissibles of other free interior facets is "
			      << common_otherfacets_coveradmissibles << std::endl
			      << message::unlock;
    if (common_otherfacets_coveradmissibles.superset(coversimps[freefacet])) {
      MessageStreams::debug() << message::lock
			      << "coversimps " << coversimps[freefacet]
			      << " of freefacet " << freefacet << " not tightened" << std::endl
			      << message::unlock;
      return false;
    }
    coversimps[freefacet] *= common_otherfacets_coveradmissibles;
    if (coversimps[freefacet].empty()) {
      MessageStreams::debug() << message::lock
			      << "no coversimp of " << freefacet
			      << " is in coveradmissibles of all other free interior facets: no extension possible." << std::endl
			      << message::unlock;
      if (_iter_coversimptighten > _maxiter_coversimptighten) {
	_maxiter_coversimptighten = _iter_coversimptighten;
      }
      no_extension_possible = true;
      return true;
    }
    MessageStreams::debug() << message::lock
			    << "continue with updated coversimps of " << freefacet << std::endl
			    << message::unlock;
    return true;
  }

  bool SymmetricExtensionGraph::_update_coveradmissibles(const PartialTriang&            current_partial_triang,
							 const Simplex&                  freefacet,
							 seg_coversimps_type&            coversimps,
							 seg_coveradmissibles_type&      coveradmissibles,
							 seg_coversimps_flag_type&       these_coversimps_changed,
							 seg_coveradmissibles_flag_type& these_coveradmissibles_changed) const {
    these_coveradmissibles_changed[freefacet] = false;
    if (!these_coversimps_changed[freefacet]) {
      return false;
    }
    MessageStreams::debug() << message::lock
			    << "updating coveradmissibles of free interior facet " << freefacet << " ..." << std::endl
			    << message::unlock;
    coveradmissibles[freefacet].clear();
    for (SimplicialComplex::const_iterator coveriter = coversimps[freefacet].begin();
	 coveriter != coversimps[freefacet].end();
	 ++coveriter) {
#ifdef TOPCOM_CONTAINERS
      coveradmissibles[freefacet] += current_partial_triang.admtableptr()->operator[](*coveriter);
#else      
      coveradmissibles[freefacet] += current_partial_triang.admtableptr()->find(*coveriter)->second;
#endif
    }
    return true;
  }

  bool SymmetricExtensionGraph::_some_possiblecoversimps_empty(const PartialTriang&            current_partial_triang,
							       seg_coversimps_type&            coversimps,
							       seg_coveradmissibles_type&      coveradmissibles,
							       seg_coversimps_flag_type&       these_coversimps_changed,
							       seg_coveradmissibles_flag_type& these_coveradmissibles_changed) const {
    MessageStreams::debug() << message::lock
			    << "try to tighten coversimplices of free interior facets "
			    << current_partial_triang.freeintfacets() << " ..." << std::endl
			    << message::unlock;
    bool some_coversimps_changed(true);
    _iter_coversimptighten = 0;
    while (some_coversimps_changed) {
      ++_iter_coversimptighten;
      for (SimplicialComplex::const_iterator ffiter = current_partial_triang.freeintfacets().begin();
	   ffiter != current_partial_triang.freeintfacets().end();
	   ++ffiter) {
	const Simplex& freefacet(*ffiter);
	these_coveradmissibles_changed[freefacet] =_update_coveradmissibles(current_partial_triang,
									    freefacet,
									    coversimps,
									    coveradmissibles,
									    these_coversimps_changed,
									    these_coveradmissibles_changed);
      }
#ifdef USE_CONFFACETS    
      for (facets_data::const_iterator ffiter = current_partial_triang.freeconffacets().begin();
      	   ffiter != current_partial_triang.freeconffacets().end();
      	   ++ffiter) {
      	const Simplex& freefacet(*ffiter);
      	these_coveradmissibles_changed[freefacet] =_update_coveradmissibles(current_partial_triang,
      									    freefacet,
      									    coversimps,
      									    coveradmissibles,
      									    these_coversimps_changed,
      									    these_coveradmissibles_changed);
      }
#endif
      some_coversimps_changed = false;
      bool no_extension_possible = false;
      for (SimplicialComplex::const_iterator ffiter = current_partial_triang.freeintfacets().begin();
	   ffiter != current_partial_triang.freeintfacets().end();
	   ++ffiter) {
	const Simplex& freefacet(*ffiter);
	these_coversimps_changed[freefacet] = _update_coversimps(current_partial_triang,
								 freefacet,
								 coversimps,
								 coveradmissibles,
								 these_coversimps_changed,
								 these_coveradmissibles_changed,
								 no_extension_possible);
	if (no_extension_possible) {
	  return true;
	}
	if (these_coversimps_changed[freefacet]) {
	  some_coversimps_changed = true;
	}
      }
#ifdef USE_CONFFACETS    
      for (facets_data::const_iterator ffiter = current_partial_triang.freeconffacets().begin();
      	   ffiter != current_partial_triang.freeconffacets().end();
      	   ++ffiter) {
      	const Simplex& freefacet(*ffiter);
      	these_coversimps_changed[freefacet] = _update_coversimps(current_partial_triang,
      								 freefacet,
      								 coversimps,
      								 coveradmissibles,
      								 these_coversimps_changed,
      								 these_coveradmissibles_changed,
      								 no_extension_possible);
      	if (no_extension_possible) {
      	  return true;
      	}
      	if (these_coversimps_changed[freefacet]) {
      	  some_coversimps_changed = true;
      	}
      }
#endif
    }
    MessageStreams::debug() << message::lock
			    <<  "... no further tightening possible" << std::endl
			    << message::unlock;
    return false;
  }

  bool SymmetricExtensionGraph::_some_possibleinitcoversimps_empty(const PartialTriang&            current_partial_triang,
								   seg_coversimps_type&            coversimps,
								   seg_coveradmissibles_type&      coveradmissibles,
								   seg_coversimps_flag_type&       these_coversimps_changed,
								   seg_coveradmissibles_flag_type& these_coveradmissibles_changed) const {
    MessageStreams::debug() << message::lock
			    << "generating all coversimplices of free interior facets for current_partial_triang "
			    << current_partial_triang << " ..." << std::endl
			    << message::unlock;
    for (SimplicialComplex::const_iterator ffiter = current_partial_triang.freeintfacets().begin();
	 ffiter != current_partial_triang.freeintfacets().end();
	 ++ffiter) {
      const Simplex& freeintfacet(*ffiter);
      const seg_coversimps_type::iterator coversimp_iter
	= coversimps.insert(std::pair<Simplex, SimplicialComplex>(freeintfacet,
								  current_partial_triang.inctableptr()->intcofacets(freeintfacet)
								  * current_partial_triang.admissibles()
								  )
			    ).first;
      if (coversimp_iter->second.empty()) {
	MessageStreams::debug() << message::lock
				<< "coversimplices of free interior facet " << freeintfacet
				<< " empty: no extension possible" << std::endl
				<< message::unlock;
	return true;
      }
      these_coversimps_changed[freeintfacet] = true;
    }
#ifdef USE_CONFFACETS    
    MessageStreams::debug() << message::lock
    			    << "generating all coversimplices of free configuration facets "
    			    << current_partial_triang.freeconffacets()
    			    << " for current_partial_triang "
    			    << current_partial_triang << " ..." << std::endl
    			    << message::unlock;    
    for (facets_data::const_iterator ffiter = current_partial_triang.freeconffacets().begin();
    	 ffiter != current_partial_triang.freeconffacets().end();
    	 ++ffiter) {
      const Simplex& freeconffacet(*ffiter);
      const seg_coversimps_type::iterator coversimp_iter
    	= coversimps.insert(std::pair<Simplex, SimplicialComplex>(freeconffacet,
    								  current_partial_triang.inctableptr()->confcofacets(freeconffacet)
    								  * current_partial_triang.admissibles()
    								  )
    			    ).first;
      if (coversimp_iter->second.empty()) {
    	MessageStreams::debug() << message::lock
    				<< "coversimplices of free configuration facet " << freeconffacet
    				<< " empty: no extension possible" << std::endl
    				<< message::unlock;
    	return true;
      }
      these_coversimps_changed[freeconffacet] = true;
    }
#endif
    MessageStreams::debug() << message::lock
			    << "... done - no obstruction so far" << std::endl
			    << message::unlock;
    return false;
  }

  bool SymmetricExtensionGraph::_noncoverable_freefacet_lex(const PartialTriang& current_partial_triang) const {

    // we have to check beforehand that neither admissibles nor freefacets are empty:
    const Simplex min_coverable = current_partial_triang.admissibles().begin()->lexmin_subset(_rank - 1);
    if (CommandlineOptions::debug()) {
      MessageStreams::debug() << message::lock
			      << *current_partial_triang.freeintfacets().begin()
			      << " < "
			      << min_coverable
			      << "?" << std::endl
			      << message::unlock;
    }
    if (current_partial_triang.freeintfacets().begin()->lexsmaller(min_coverable)) {
      if (CommandlineOptions::debug()) {
	MessageStreams::debug() << message::lock
				<< *current_partial_triang.freeintfacets().begin()
				<< " < "
				<< min_coverable << std::endl
				<< message::unlock;
      }
      return true;
    }
    else {
      if (CommandlineOptions::debug()) {
	MessageStreams::debug() << message::lock
				<< *current_partial_triang.freeintfacets().begin()
				<< " not < "
				<< min_coverable << std::endl
				<< message::unlock;
      }
      return false;
    }
  }

  bool SymmetricExtensionGraph::_noncoverable_freefacet_lean(const PartialTriang& current_partial_triang) const {
    MessageStreams::debug() << message::lock
			    << "lean search for a non coverable interior facet in "
			    << current_partial_triang
			    << " ..." << std::endl
			    << message::unlock;
    for (SimplicialComplex::const_iterator ffiter = current_partial_triang.freeintfacets().begin();
	 ffiter != current_partial_triang.freeintfacets().end();
	 ++ffiter) {
      const Simplex& freeintfacet(*ffiter);
      if ((current_partial_triang.inctableptr()->intcofacets(freeintfacet) * current_partial_triang.admissibles()).empty()) {
	MessageStreams::debug() << message::lock
				<< "coversimplices of free interior facet "
				<< freeintfacet
				<< " empty: no extension possible" << std::endl
				<< message::unlock;
	return true;
      }
    }
#ifdef USE_CONFFACETS    
    for (facets_data::const_iterator ffiter = current_partial_triang.freeconffacets().begin();
    	 ffiter != current_partial_triang.freeconffacets().end();
    	 ++ffiter) {
      const Simplex& freeconffacet(*ffiter);
      if ((current_partial_triang.inctableptr()->confcofacets(freeconffacet) * current_partial_triang.admissibles()).empty()) {
    	MessageStreams::debug() << message::lock
    				<< "coversimplices of free configuration facet "
    				<< freeconffacet
    				<< " empty: no extension possible" << std::endl
    				<< message::unlock;
    	return true;
      }
    }
#endif
    MessageStreams::debug() << message::lock
			    << "... done - no obstruction so far" << std::endl
			    << message::unlock;
    return false;
  }

  bool SymmetricExtensionGraph::_noncoverable_freefacet_strong(const PartialTriang& current_partial_triang) const {
    bool result(false);
    seg_coversimps_type coversimps;
    seg_coveradmissibles_type coveradmissibles;
    seg_coversimps_flag_type these_coversimps_changed;
    seg_coveradmissibles_flag_type these_coveradmissibles_changed;

    if (_some_possibleinitcoversimps_empty(current_partial_triang,
					   coversimps,
					   coveradmissibles,
					   these_coversimps_changed,
					   these_coveradmissibles_changed)) {
      return true;
    }
    if (_some_possiblecoversimps_empty(current_partial_triang,
				       coversimps,
				       coveradmissibles,
				       these_coversimps_changed,
				       these_coveradmissibles_changed)) {
      MessageStreams::debug() << message::lock
			      << "*** strong obstruction search successful - no extension possible ***" << std::endl
			      << message::unlock;
      return true;
    }
    return false;
  }
  
  bool SymmetricExtensionGraph::_noncoverable_freefacet(const PartialTriang& current_partial_triang) const {
    if (CommandlineOptions::no_extension_check()) {
      return false;
    }
    if (CommandlineOptions::full_extension_check()) {
      return this->_noncoverable_freefacet_strong(current_partial_triang);
    }
    else if (CommandlineOptions::use_volume_order() || CommandlineOptions::use_random_order()) {
      return this->_noncoverable_freefacet_lean(current_partial_triang);
    }
    else {
      return this->_noncoverable_freefacet_lex(current_partial_triang);
    }
  }
  
  bool SymmetricExtensionGraph::_not_enough_volume(const PartialTriang& current_partial_triang) const {
    if (CommandlineOptions::symmetries_are_isometric() || _symmetriesptr->empty()) {
      Field missing_volume = _volume - current_partial_triang.covered_volume();
      SimplicialComplex::const_iterator iter = current_partial_triang.admissibles().begin();
      MessageStreams::debug() << message::lock
			      << "still "
			      << current_partial_triang.admissibles().card()
			      << " simplices available to cover an uncovered volume of "
			      << missing_volume
			      << " ..." << std::endl
				<< message::unlock;
      while (missing_volume > FieldConstants::ZERO) {
	if (iter == current_partial_triang.admissibles().end()) {
	  return true;
	}
	missing_volume -= current_partial_triang.voltableptr()->find(*iter)->second;
	++iter;
	MessageStreams::debug() << message::lock
				<< "still "
				<< current_partial_triang.admissibles().card()
				<< " simplices available to cover an uncovered volume of "
				<< missing_volume
				<< " ..." << std::endl
				<< message::unlock;
      }
      return false;
    }
    else {
      return false;
    }
  }
  
  size_type SymmetricExtensionGraph::_min_no_of_missing_simplices(const PartialTriang& current_partial_triang) const {
    const size_type card_bound =  (current_partial_triang.freeintfacets().card()
#ifdef USE_CONFFACETS    
				   +
				   current_partial_triang.freeconffacets().size()
#endif
				   + _rank - 1) / _rank;
    if (CommandlineOptions::use_volume_order() && CommandlineOptions::use_volumes()) {
      
      // in this case, we can rely on current_partial_triang.admissibles() to be sorted by volume:
      Field missing_volume = _volume;
      missing_volume -= current_partial_triang.covered_volume();
      size_type result = 0UL;
      SimplicialComplex::const_iterator iter = current_partial_triang.admissibles().begin();
      while (missing_volume > FieldConstants::ZERO) {
	if (iter == current_partial_triang.admissibles().end()) {
	  return std::numeric_limits<size_type>::max();
	}
	missing_volume -= current_partial_triang.voltableptr()->find(*iter)->second;
	++result;
	++iter;
      }
      return std::max<size_type>(result, card_bound);
    }
    else {
      return card_bound;
    }
  }
  
  void SymmetricExtensionGraph::_init() {
    if (CommandlineOptions::output_asy()) {
      Graphics::run_to_asy(_ID);
      Graphics::partialtriang_to_asy(_ID, _runID, _rootptr->partial_triang().index_set_pure(), _rootptr->partial_triang());
    }
    if (CommandlineOptions::parallel_symmetries()) {
      _no_of_threads = CommandlineOptions::no_of_threads();
      _init_symmetry_workers();
    }
    if (_print_triangs) {
      if (CommandlineOptions::debug()) {
	_triang_outputter.activate_debug();
      }
      else {
	_triang_outputter.activate();
      }
    }
    
    // from here on we need to block IO:
    MessageStreams::debug() << message::lock
			    << "exploring all symmetry classes of triangulations by extension ..." << '\n'
			    << message::unlock;
    MessageStreams::debug() << message::lock
			    << "SymmetricExtensionGraph:" << '\n'
			    << "root triangulation: " << _rootptr->partial_triang() << '\n'
			    << "freeintfacets     : " << _rootptr->partial_triang().freeintfacets() << '\n'
			    << "admissibles       : " << _rootptr->partial_triang().admissibles() << '\n'
			    << "admissibles table : " << *(_rootptr->partial_triang().admtableptr()) << '\n'
			    << "incidences        : " << *(_rootptr->partial_triang().inctableptr()) << std::endl;
    if (_rootptr->partial_triang().voltableptr()) {
      MessageStreams::debug() << "volumes           : " << *(_rootptr->partial_triang().voltableptr()) << std::endl;
    }
    MessageStreams::debug() << message::unlock;
 }
  
  void SymmetricExtensionGraph::_init_symmetry_workers() {
    MessageStreams::debug() << message::lock
			    << "initialize distributed symmetry workers ..." << std::endl
			    << message::unlock;
    for (int i = 0; i < _no_of_threads; ++i) {
      MessageStreams::debug() << message::lock
			      << message::tab
			      << "initializing symmetry worker " << i
			      << " ..." << std::endl
			      << message::unlock;
      _symmetry_workers.emplace_back(i, *this);
      MessageStreams::debug() << message::lock
			      << message::tab
			      << "... done" << std::endl
			      << message::unlock;
    }
    for (int i = 0; i < _no_of_threads; ++i) {
      MessageStreams::debug() << message::lock
			      << message::tab
			      << "starting symmetry worker thread " << i 
			      << " ..." << std::endl
			      << message::unlock;
      _threads.push_back(std::thread(&SymmetricExtensionGraph::SymmetryWorker::operator(),
				     std::ref(_symmetry_workers[i])));
      MessageStreams::debug() << message::lock
			      << message::tab
			      << "... done" << std::endl
			      << message::unlock;
    }
    MessageStreams::debug() << message::lock
			    << "... done" << std::endl
			    << message::unlock;
  }
  
  void SymmetricExtensionGraph::_bfs(const seg_nodes_container_type&  current_nodes,
				     size_type&                       depth) {
    SimplicialComplex forbidden;
    // seg_triang_container_type new_partial_triangs;
    ++depth;
    _progress_vector.resize(depth);
    size_type doneload(0UL);
    const size_type workload(current_nodes.size());
    MessageStreams::debug() << message::lock
			    << "upsizing progress vector to " << depth << " ..." << std::endl
			    << message::unlock;
    for (seg_nodes_container_type::const_iterator iter = current_nodes.begin();
	 iter != current_nodes.end();
	 ++iter) {
      ++doneload;
      _progress_vector.at(depth - 1) = (100 * doneload) / workload;
      
      const node_type current_node(*iter);
      const PartialTriang& current_partial_triang(current_node.partial_triang());
      MessageStreams::debug() << message::lock
			      << "processing partial triang "
			      << current_partial_triang << " ..." << '\n'
			      << "still possible extension simplices "
			      << current_partial_triang.admissibles() << std::endl
			      << message::unlock;
    
      // loop over all admissible extensions:
      for (SimplicialComplex::const_iterator simpiter = current_partial_triang.admissibles().begin();
	   simpiter != current_partial_triang.admissibles().end();
	   ++simpiter) {
	++this->_nodecount;
	report_progress(MessageStreams::verbose());
	const Simplex& new_simp(*simpiter);
	forbidden += new_simp;
	MessageStreams::debug() << message::lock
				<< "adding " << new_simp << " to " << current_partial_triang << " ..." << std::endl
				<< message::unlock;
	PartialTriang next_partial_triang(current_partial_triang, new_simp, forbidden);
	MessageStreams::debug() << message::lock
				<< "resulting in " << next_partial_triang << '\n'
				<< next_partial_triang << '\n'
				<< "next free facets" << '\n'
				<< next_partial_triang.freeintfacets() << std::endl
				<< message::unlock;
      
	// since the following check is faster than checking for an old symmetry class,
	// we prepend it here - check whether we are in a dead end:
	if (!next_partial_triang.freeintfacets().empty()) {
	
	  // not yet a triangulation - perform the extendability checks:
	  if (next_partial_triang.admissibles().empty()) {
	  
	    // we are in a dead end:
	    MessageStreams::debug() << message::lock
				    << "reached maximal non-triangulation:" << '\n'
				    << next_partial_triang << std::endl
				    << message::unlock;
	  
	    ++this->_deadendcount;
	    continue;
	  }
	
	  // the following check is faster than to check for an equivalent partial triangulation:
	  // check whether the partial triangulation can possibly be extended:
	  if (this->_noncoverable_freefacet(next_partial_triang)) {
	  
	    //early detected dead end:
	    MessageStreams::debug() << message::lock
				    << "no full-triangulation extension possible of "
				    << next_partial_triang <<  std::endl
				    << message::unlock;
	    ++this->_earlydeadendcount;
	    continue;
	  }
	
	  // another fast check:
	  // check whether there is still enough volume in the possible admissible simplices:
	  if (CommandlineOptions::use_volumes()) {
	    if (this->_not_enough_volume(next_partial_triang)) {
	    
	      // early detected dead end:
	      MessageStreams::debug() << message::lock
				      << "volume obstruction hit - no extension possible of "
				      << next_partial_triang << std::endl
				      << message::unlock;
	      ++this->_earlydeadendcount;
	      continue;
	    }
	  }
	}
      
	// now the more expensive symmetry check (necessary also for triangulations):
	MessageStreams::debug() << message::lock
				<< "checking for old symmetry class ..." << std::endl
				<< message::unlock;
	bool is_new = true;
	critical_simpidx_table_type new_critsimp_table;
	size_type new_stabilizer_card = 0UL;
	if (CommandlineOptions::use_classified_symmetries()) {

	  // use the method of TOPCOM-1.0.0:
	  is_new = !_old_symmetry_class(next_partial_triang, new_simp);
	}
	else {

	  // use the method of TOPCOM-1.0.1 and above:
	  if (!_simpidx_symmetriesptr) {
	    is_new = current_node.child_is_lexmin_lean(new_simp, &new_critsimp_table, &new_stabilizer_card);
	  }
	  else {
	    is_new = current_node.child_is_lexmin(new_simp, &new_critsimp_table, &new_stabilizer_card);
	  }
	}
      
	if (!is_new) {
	  MessageStreams::debug() << message::lock
				  << next_partial_triang << " was found already: continue" << std::endl
				  << message::unlock;
	  continue;
	}
	else {
	  MessageStreams::debug() << message::lock
				  << next_partial_triang << " was not found yet: process" << std::endl
				  << message::unlock;
	
	  // next partial triang is new:
	  if (next_partial_triang.freeintfacets().empty()) {
	  
	    if (CommandlineOptions::debug()) {
	    
	      // next partial triang is a complete triang –
	      // double-check required symmetries
	      // (should be taken care of by the stripped admissibles):
	      if (!_required_symmetriesptr->fixes(next_partial_triang)) {
		MessageStreams::debug() << message::lock
					<< next_partial_triang
					<< " does not have required symmetries - ignore" << std::endl
					<< message::unlock;
		continue;
	      }
	    }
	  
	    // check regularity if required and output the triangulation:
	    if (CommandlineOptions::check_regular()) {
	      if (CommandlineOptions::symmetries_are_affine()) {
		// here we need to check only the found representative for regularity:
		RegularityCheck regchecker(*_pointsptr, *_chiroptr, *_inctableptr, next_partial_triang);
		if (regchecker.is_regular()) {
		  _mintriang = next_partial_triang;
		  _mincard = _mintriang.card();
		  _triang_outputter.print_mintriang(_output_stream, this->_ID, _mintriang, regchecker.heights());
		  output_results(MessageStreams::result());
		  MessageStreams::verbose() << message::lock
					    << "worker " << this->_ID << " found minimal triangulation "
					    << _mintriang
					    << " with " << _mincard << " simplices" << std::endl
					    << message::unlock;
		  ++this->_symcount;
		  return;
		}
	      }
	      else {
	      
		// here we need to check the complete orbit (except the user wants to skip this):
		RegularityCheck regchecker(*_pointsptr, *_chiroptr, *_inctableptr, next_partial_triang);
		if (regchecker.is_regular()) {
		  _mintriang = next_partial_triang;
		  _mincard = _mintriang.card();
		  _triang_outputter.print_mintriang(_output_stream, this->_ID, _mintriang, regchecker.heights());
		  output_results(MessageStreams::result());
		  MessageStreams::verbose() << message::lock
					    << "worker " << this->_ID << " found minimal triangulation "
					    << _mintriang << " with " << _mincard << " simplices" << std::endl
					    << message::unlock;
		  ++this->_symcount;
		  return;
		}
		if (!CommandlineOptions::skip_orbitcount()) {
		  for (SymmetryGroup::const_iterator symiter = _symmetriesptr->begin();
		       symiter != _symmetriesptr->end();
		       ++symiter) {
		    const Symmetry& g(*symiter);
		    const pt_complex_type equivalent_partial_triang(g.map(next_partial_triang));
		    if (_orbit.find(equivalent_partial_triang) == _orbit.end()) {
		      RegularityCheck regchecker(*_pointsptr, *_chiroptr, *_inctableptr, equivalent_partial_triang);
		      if (regchecker.is_regular()) {
			_mintriang = equivalent_partial_triang;
			_mincard = _mintriang.card();
			_triang_outputter.print_mintriang(_output_stream, this->_ID, _mintriang, regchecker.heights());
			output_results(MessageStreams::result());
			MessageStreams::verbose() << message::lock
						  << "worker " << this->_ID << " found minimal triangulation "
						  << _mintriang
						  << " with " << _mincard << " simplices" << std::endl
						  << message::unlock;
			++this->_symcount;
			return;
		      }
		    }
		  }
		}
	      }
	    }
	    else {
	      _mintriang = next_partial_triang;
	      _mincard = _mintriang.card();
	      _triang_outputter.print_mintriang(_output_stream, this->_ID, next_partial_triang.simplicial_complex());
	      output_results(MessageStreams::result());
	      MessageStreams::verbose() << "worker " << this->_ID << " found minimal triangulation "
					<< _mintriang
					<< " with " << _mincard << " simplices" << std::endl;
	      ++_symcount;
	      ++_totalcount;
	      return;
	    }
	  }
	  else if (next_partial_triang.admissibles().empty()) {
	  
	    // dead end because we ran out of admissible simplices to extend the partial triangulation:
	    MessageStreams::debug() << message::lock
				    << "reached maximal non-triangulation:" << '\n'
				    << next_partial_triang << std::endl
				    << message::unlock;
	  
	    ++this->_deadendcount;
	  }
	  else {
	  
	    // partial triang - move node into queue for later processing:
	    _open_nodes.emplace_back(current_node, std::move(next_partial_triang), std::move(new_critsimp_table));
	  }
	}
      }
      forbidden.clear();
    }
  }
  
  void SymmetricExtensionGraph::_dfs(const node_type& current_node,
				     size_type&       depth) {
    
    SimplicialComplex forbidden;
    const PartialTriang& current_partial_triang(current_node.partial_triang());
    MessageStreams::debug() << message::lock
			    << "extending the following partial triangulation:" << '\n'
			    << "partial triangulation: " << current_partial_triang << '\n'
			    << "freeintfacets        : " << current_partial_triang.freeintfacets() << '\n'
			    << "admissibles          : " << current_partial_triang.admissibles() << '\n'
			    << "admissibles table    : " << *current_partial_triang.admtableptr() << '\n'
			    << "incidences           : " << *current_partial_triang.inctableptr() << std::endl;
    if (_rootptr->partial_triang().voltableptr()) {
      MessageStreams::debug() << "volumes              : " << *current_partial_triang.voltableptr() << std::endl;
    }
    MessageStreams::debug() << message::unlock;
    const size_type workload(current_partial_triang.admissibles().card());  
    size_type doneload(0UL);
    ++depth;
    MessageStreams::debug() << message::lock
			    << "upsizing progress vector to " << depth << " ..." << std::endl
			    << message::unlock;
    _progress_vector.resize(depth);
    
    MessageStreams::debug() << message::lock
			    << "D|A|I|C -> "
			    << std::setw(4) << depth << "|"
			    << std::setw(4) << workload << "|"
			    << std::setw(4) << current_partial_triang.freeintfacets().card() << "|"
#ifdef USE_CONFFACETS    
			    << std::setw(4) << current_partial_triang.freeconffacets().size()
#endif
			    << std::endl
			    << message::unlock;
    
    // for asy output:
    size_type parent_node_ID = this->_nodecount;
    
    // loop over all admissible extensions:
    for (SimplicialComplex::const_iterator simpiter = current_partial_triang.admissibles().begin();
	 simpiter != current_partial_triang.admissibles().end();
	 ++simpiter) {
      ++doneload;
      _progress_vector.at(depth - 1) = (100 * doneload) / workload;
      ++this->_nodecount;
      const Simplex& new_simp(*simpiter);
      
      // for asy output:
      size_type child_node_ID = this->_nodecount;
      
      if (CommandlineOptions::output_asy()) {
	SimplicialComplex next_partial_triang(current_partial_triang + new_simp);
	Graphics::partialtriang_to_asy(_ID, _runID, next_partial_triang.index_set_pure(), next_partial_triang);
	Graphics::arc_to_asy(_ID, _runID, parent_node_ID, child_node_ID);
      }      
      report_progress(MessageStreams::verbose());
      if (!CommandlineOptions::no_extension_check()
	  // && !CommandlineOptions::full_extension_check()
	  && !CommandlineOptions::use_volume_order()
	  && !CommandlineOptions::use_random_order()) {
	
	// in this lex-order case, we can check whether the new simplex is able to cover the minimal uncovered facets;
	// if not, neither this new simplex nor the upcoming ones will cover it:
	const Simplex min_coverable = new_simp.lexmin_subset(_rank - 1);
	if (!current_partial_triang.freeintfacets().empty() && current_partial_triang.freeintfacets().begin()->lexsmaller(min_coverable)) {
	  if (CommandlineOptions::debug()) {
	    MessageStreams::debug() << message::lock
				    << *current_partial_triang.freeintfacets().begin()
				    << " < "
				    << min_coverable << std::endl
				    << message::unlock;
	  }
	  
	  // for asy output:
	  if (CommandlineOptions::output_asy()) {
	    Graphics::veryearlydeadend_to_asy(_ID, _runID, child_node_ID);
	  }
	  
	  ++this->_earlydeadendcount;
	  break;
	}
      }
      forbidden += new_simp;
      MessageStreams::debug() << message::lock
			      << "adding " << new_simp << " to "
			      << current_partial_triang << " ..." << '\n'
			      << "node " << this->_nodecount << ": checking new partial triangulation:" << '\n'
			      << "current free facets" << '\n'
			      << current_partial_triang.freeintfacets() << std::endl
			      << message::unlock;
      
      // construct the extended partial triangulation:
      PartialTriang next_partial_triang(current_partial_triang, new_simp, forbidden);
      MessageStreams::debug() << message::lock
			      << "resulting in " << next_partial_triang << '\n'
			      << next_partial_triang << '\n'
			      << "next free facets" << '\n'
			      << next_partial_triang.freeintfacets() << std::endl
			      << message::unlock;
      
      // check lower bound for minimality if required:
      if (this->_find_minimal_triang) {
	size_type min_no_of_simplices = depth + this->_min_no_of_missing_simplices(next_partial_triang);
	std::lock_guard<std::mutex> min_lock(_min_mutex);
	if (min_no_of_simplices >= this->_mincard) {
	  MessageStreams::debug() << message::lock
				  << "current minimal cardinality is " << this->_mincard << '\n'
				  << "minimal no. of simplices is    " << min_no_of_simplices << '\n'
				  << " => prune" << std::endl
				  << message::unlock;
	  continue;
	}
      }
      
      // check target no of simplices if required:
      size_type target_no_of_simplices = 0;
      if (CommandlineOptions::max_no_of_simplices() > 0) {
	target_no_of_simplices = CommandlineOptions::max_no_of_simplices();
      }
      if (CommandlineOptions::no_of_simplices() > 0) {
	target_no_of_simplices = CommandlineOptions::no_of_simplices();
      }
      if (target_no_of_simplices > 0) {
	size_type min_no_of_simplices = depth + this->_min_no_of_missing_simplices(next_partial_triang);
	if (min_no_of_simplices > target_no_of_simplices) {
	  MessageStreams::debug() << message::lock
				  << "current no. of simplices = " << depth << '\n'
				  << "minimal number of missing simplices = " << min_no_of_simplices << '\n'
				  << "=> target no. of simplices " << target_no_of_simplices << " unreachable => prune" << std::endl
				  << message::unlock;
	  continue;
	}
      }
      
      // since the following check is faster than checking for an old symmetry class,
      // we prepend it here - check whether we are in a dead end:
      if (!next_partial_triang.freeintfacets().empty()) {
	
	// not yet a triangulation - perform the extendability checks:
	if (next_partial_triang.admissibles().empty()) {
	  
	  // we are in a dead end:
	  MessageStreams::debug() << message::lock
				  << "freeintfacets: " << next_partial_triang.freeintfacets() << '\n'
				  << "admissibles  : " << next_partial_triang.admissibles() << '\n'
				  << "reached maximal non-triangulation:" << '\n'
				  << next_partial_triang << std::endl
				  << message::unlock;
	  
	  // for asy output:
	  if (CommandlineOptions::output_asy()) {
	    Graphics::deadend_to_asy(_ID, _runID, child_node_ID);
	  }
	  
	  ++this->_deadendcount;
	  continue;
	}
	
	// the following checks are faster than checking for a lex-smaller partial triangulation,
	// thus, they are performed first:
	// check whether we can extend current_partial_triang at all free facets:
	MessageStreams::debug() << message::lock
				<< "cover obstruction check for "
				<< next_partial_triang << std::endl
				<< message::unlock;
	
	// the following check is faster than to check for an equivalent partial triangulation:
	// check whether the partial triangulation can possibly be extended:
	if (this->_noncoverable_freefacet(next_partial_triang)) {
	  
	  // early detected dead end:
	  MessageStreams::debug() << message::lock
				  << "cover obstruction hit - no extension possible of "
				  << next_partial_triang << std::endl
				  << message::unlock;
	  
	  // for asy output:
	  if (CommandlineOptions::output_asy()) {
	    Graphics::earlydeadend_to_asy(_ID, _runID, child_node_ID);
	  }
	  
	  ++this->_earlydeadendcount;
	  continue;
	}
	
	// another fast check:
	// check whether there is still enough volume in the possibly extending simplices:
	if (CommandlineOptions::use_volumes() && !this->_find_minimal_triang) {
	  MessageStreams::debug() << message::lock
				  << "volume obstruction check for "
				  << next_partial_triang << std::endl
				  << message::unlock;
	  if (this->_not_enough_volume(next_partial_triang)) {
	    
	    // early detected dead end:
	    MessageStreams::debug() << message::lock
				    << "volume obstruction hit - no extension possible of "
				    << next_partial_triang << std::endl
				    << message::unlock;
	    
	    // for asy output:
	    if (CommandlineOptions::output_asy()) {
	      Graphics::missingvolume_to_asy(_ID, _runID, child_node_ID);
	    }
	    
	    ++this->_earlydeadendcount;
	    continue;
	  }
	}
      }
      
      // now we must check whether we really have found something new (also for triangulations):
      MessageStreams::debug() << message::lock
			      << "checking for old symmetry class ..." << std::endl
			      << message::unlock;
      bool is_new = true;
      critical_simpidx_table_type new_critsimp_table;
      size_type new_stabilizer_card = 0UL;
      if (CommandlineOptions::use_classified_symmetries() || CommandlineOptions::use_switch_tables() || CommandlineOptions::use_naive_symmetries()) {
	
	// use the method of TOPCOM-1.0.0:
	is_new = !_old_symmetry_class(next_partial_triang, new_simp);
      }
      else {
	
	// use the method of TOPCOM-1.0.1 and above:
	if (!_simpidx_symmetriesptr) {
	  is_new = current_node.child_is_lexmin_lean(new_simp, &new_critsimp_table, &new_stabilizer_card);
	}
	else {
	  is_new = current_node.child_is_lexmin(new_simp, &new_critsimp_table, &new_stabilizer_card);
	}
      }
      
      if (!is_new) {
	
	// for asy output:
	if (CommandlineOptions::output_asy()) {
	  Graphics::notnew_to_asy(_ID, _runID, child_node_ID);
	}	
	MessageStreams::debug() << message::lock
				<< next_partial_triang << " was found already: continue" << std::endl
				<< message::unlock;
	continue;
      }
      else {
	MessageStreams::debug() << message::lock
				<< next_partial_triang << " was not found yet: process" << std::endl
				<< message::unlock;
      }
      
      // next partial triang is new - check for completeness:
      if (next_partial_triang.freeintfacets().empty()) {
	
	// we found a complete triangulation - check regularity if required and output the triangulation:
	
	// check lower bound for minimality again using the lower bound computed above:
	if (this->_find_minimal_triang) {
	  std::lock_guard<std::mutex> min_lock(_min_mutex);
	  if (depth >= this->_mincard) {
	    MessageStreams::debug() << message::lock
				    << "current minimal cardinality is " << this->_mincard << '\n'
				    << "current no. of simplices is    " << depth << '\n'
				    << " => ignore" << std::endl
				    << message::unlock;
	    continue;
	  }
	}
	
	// check target no of simplices if required:
	if (((CommandlineOptions::no_of_simplices() > 0) && (depth != CommandlineOptions::no_of_simplices()))
	    || ((CommandlineOptions::max_no_of_simplices() > 0) && (depth > CommandlineOptions::max_no_of_simplices()))) {
	  MessageStreams::debug() << message::lock
				  << "current no. of simplices = " << depth << '\n'
				  << "target no. of simplices = " << CommandlineOptions::no_of_simplices() << '\n'
				  << "=> ignore" << std::endl
				  << message::unlock;	
	  continue;
	}
	
	if (CommandlineOptions::debug()) {
	  
	  // next partial triang is a complete triang –
	  // double-check required symmetries
	  // (should be taken care of by the stripped admissibles):
	  if (!_required_symmetriesptr->fixes(next_partial_triang)) {
	    MessageStreams::debug() << message::lock
				    << next_partial_triang << " does not have required symmetries - ignore" << std::endl
				    << message::unlock;
	    continue;
	  }
	}
	
	if (CommandlineOptions::check_regular()) {
	  if (CommandlineOptions::symmetries_are_affine()) {
	    
	    // here we need to check only the found representative for regularity:
	    RegularityCheck repregchecker(*_pointsptr, *_chiroptr, *_inctableptr, next_partial_triang);
	    if (repregchecker.is_regular()) {
	      ++this->_symcount;
	      {
		std::lock_guard<std::mutex> min_lock(_min_mutex);
		if (depth < this->_mincard) {
		  this->_mintriang = next_partial_triang;
		  this->_mincard = depth;
		}
	      }
	      _triang_outputter.print_triang(_output_stream,
					     this->_ID,
					     this->_runID,
					     this->_symcount + this->_current_symcount,
					     next_partial_triang.simplicial_complex(),
					     repregchecker.heights());
	      output_results(MessageStreams::result());
	      if (!CommandlineOptions::skip_orbitcount()) {
		
		// use the stabilizer formula:
		if (CommandlineOptions::use_switch_tables()) {
		  this->_orbitsize = _switch_tableptr->orbit_size(next_partial_triang.index_set_pure());
		}
		else if (CommandlineOptions::use_classified_symmetries() || CommandlineOptions::use_naive_symmetries()) {
		  if (!_simpidx_symmetriesptr) {
		    this->_orbitsize =
		      (this->_symmetriesptr->size() + 1)
		      / (this->_symmetriesptr->stabilizer_card(next_partial_triang) + 1);
		  }
		  else {
		    this->_orbitsize =
		      (this->_simpidx_symmetriesptr->size() + 1)
		      / (this->_simpidx_symmetriesptr->stabilizer_card(next_partial_triang.index_set_pure()) + 1);
		  }
		}
		else {
		  this->_orbitsize =
		    (this->_symmetriesptr->size() + 1)
		    / (new_stabilizer_card + 1);
		}
	      }
	    }
	  }
	  else {
	    
	    // here we need to check the complete orbit (except the user wants to skip this):
	    _orbit.insert(next_partial_triang);
	    RegularityCheck repregchecker(*_pointsptr, *_chiroptr, *_inctableptr, next_partial_triang);
	    static int count_reg = 0;
	    if (repregchecker.is_regular()) {
	      _regular_orbit.insert(next_partial_triang);
	      {
		std::lock_guard<std::mutex> min_lock(_min_mutex);
		if (depth < this->_mincard) {
		  this->_mintriang = next_partial_triang;
		  this->_mincard = depth;
		}
	      }
	      _triang_outputter.print_triang(_output_stream,
					     this->_ID,
					     this->_runID,
					     this->_symcount + this->_current_symcount,
					     next_partial_triang.simplicial_complex(),
					     repregchecker.heights());
	      output_results(MessageStreams::result());
	    }
	    else {
	      MessageStreams::debug() << message::lock
				      << "non regular: " << next_partial_triang << std::endl
				      << message::unlock;
	    }
	    if (!CommandlineOptions::skip_orbitcount()) {
	      for (SymmetryGroup::const_iterator symiter = _symmetriesptr->begin();
		   symiter != _symmetriesptr->end();
		   ++symiter) {
		const Symmetry& g(*symiter);
		const pt_complex_type equivalent_partial_triang(g.map(next_partial_triang));
		if (_orbit.find(equivalent_partial_triang) == _orbit.end()) {
		  
		  // not yet processed element in orbit:
		  _orbit.insert(equivalent_partial_triang);
		  RegularityCheck orbregchecker(*_pointsptr, *_chiroptr, *_inctableptr, equivalent_partial_triang);
		  if (orbregchecker.is_regular()) {
		    this->_regular_orbit.insert(equivalent_partial_triang);
		    {
		      std::lock_guard<std::mutex> min_lock(_min_mutex);
		      if (depth < this->_mincard) {
			this->_mintriang = equivalent_partial_triang;
			this->_mincard = depth;
		      }
		    }
		    _triang_outputter.print_triang(_output_stream,
						   this->_ID,
						   this->_runID,
						   this->_symcount + this->_current_symcount,
						   equivalent_partial_triang,
						   this->_regular_orbit.size() - 1,
						   orbregchecker.heights());
		    output_results(MessageStreams::result());
		    
		  }
		  else {
		    MessageStreams::debug() << message::lock
					    << "non regular: " << equivalent_partial_triang << std::endl
					    << message::unlock;
		  }
		}
	      }
	    }
	    this->_orbitsize = this->_regular_orbit.size();
	    if (this->_orbitsize > 0) {
	      ++this->_symcount;
	    }
	  }
	  
	  // we count a symmetry class whenever one orbit element is regular:
	  this->_totalcount += this->_orbitsize;
	  this->_orbit.clear();
	  this->_regular_orbit.clear();
	}
	else {
	  if (this->_find_minimal_triang) {
	    std::lock_guard<std::mutex> min_lock(_min_mutex);	    
	    if (depth < this->_mincard) {
	      MessageStreams::debug() << message::lock
				      << "found smaller triangulation " << next_partial_triang
				      << " with " << depth << " simplices" << std::endl
				      << message::unlock;
	      this->_mintriang = next_partial_triang;
	      this->_mincard = depth;
	    }
	  }
	  _triang_outputter.print_triang(_output_stream,
					 this->_ID,
					 this->_runID,
					 this->_symcount + this->_current_symcount,
					 next_partial_triang.simplicial_complex());
	  output_results(MessageStreams::result());
	  if (!CommandlineOptions::skip_orbitcount()) {
	    
	    // use the stabilizer formula:
	    if (CommandlineOptions::use_switch_tables()) {
	      this->_orbitsize = _switch_tableptr->orbit_size(next_partial_triang.index_set_pure());
	    }
	    else if (CommandlineOptions::use_classified_symmetries() || CommandlineOptions::use_naive_symmetries()) {
	      if (!_simpidx_symmetriesptr) {
		this->_orbitsize =
		  (this->_symmetriesptr->size() + 1)
		  / (this->_symmetriesptr->stabilizer_card(next_partial_triang) + 1);
	      }
	      else {
		this->_orbitsize =
		  (this->_simpidx_symmetriesptr->size() + 1)
		  / (this->_simpidx_symmetriesptr->stabilizer_card(next_partial_triang.index_set_pure()) + 1);
	      }
	    }
	    else {
	      this->_orbitsize =
		(this->_symmetriesptr->size() + 1)
		/ (new_stabilizer_card + 1);
	    }
	  }
	  
	  // increase the count for symmetry classes:
	  ++this->_symcount;
	  this->_totalcount += this->_orbitsize;
	}
	
	// for asy output:
	if (CommandlineOptions::output_asy()) {
	  Graphics::solution_to_asy(_ID, _runID, child_node_ID);
	}	
	
	this->_orbit.clear();
	this->_orbitsize = 0;
      }
      else {
	
	// we can use regularity checks already for partial triangulations;
	// whether or not this is faster depends
	// on the number of non-regular partial triangulations:
	// if there are many, then regularity checks on all
	// partial triangulations may pay off;
	// note that this is only correct if regularity
	// is constant in orbits:
	if (CommandlineOptions::check_regular_early()
	    && CommandlineOptions::check_regular()
	    && CommandlineOptions::symmetries_are_affine()) {
	  RegularityCheck pt_regchecker(*_pointsptr, *_chiroptr, *_inctableptr, next_partial_triang);
	  if (!pt_regchecker.is_regular()) {
	    MessageStreams::debug() << message::lock
				    << next_partial_triang << " is already non-regular: continue" << std::endl
				    << message::unlock;
	    continue;
	  }
	}
	_update_continue_work();
	if (_continue_work) {	
	  MessageStreams::debug() << message::lock
				  << "entering recursion with " << next_partial_triang << std::endl
				  << message::unlock;
	  this->_dfs(node_type(current_node, std::move(next_partial_triang), std::move(new_critsimp_table)), depth);
	}
	else {
	  MessageStreams::debug() << message::lock
				  << "interrupting enumeration of worker " << _ID << " ..." << std::endl
				  << message::unlock;
	  _open_nodes.emplace_back(current_node, std::move(next_partial_triang), std::move(new_critsimp_table));
	  MessageStreams::debug() << message::lock
				  << "... done" << std::endl
				  << message::unlock;
	}
      }
    }
    --depth;
    MessageStreams::debug() << message::lock
			    << "downsizing progress vector to " << depth << " ..." << std::endl
			    << message::unlock;
    _progress_vector.resize(depth);
  }
}; // namespace topcom

// eof SymmetricExtensionGraph.cc
