//////////////////////////////////////////////////////////////////////////
// SymmetricSubsetGraph.hh
// produced: 06/02/2020 jr
/////////////////////////////////////////////////////////////////////////
#ifndef SYMMETRICSUBSETGRAPH_HH
#define SYMMETRICSUBSETGRAPH_HH

#include <iostream>
#include <sstream>
#include <iomanip>
#include <fstream>
#include <string>

#include <assert.h>
#include <vector>
#include <set>
#include <map>
#include <stack>

#include <thread>
#include <atomic>

#include "LabelSet.hh"

#include "Message.hh"
#include "CommandlineOptions.hh"

#include "StrictStairCaseMatrix.hh"
#include "StrictStairCaseMatrixTrans.hh"
#include "PointConfiguration.hh"
#include "Chirotope.hh"
#include "Circuits.hh"
#include "Cocircuits.hh"
#include "Symmetry.hh"
#include "SwitchTable.hh"
#include "ClassifiedSubsetSymmetries.hh"
#include "SymmetricSubsetGraphNode.hh"
#include "ScreenReport.hh"

namespace topcom {

  typedef LabelSet                                            subset_type;

#ifdef TOPCOM_CONTAINERS
#include "HashSet.hh"
  typedef HashSet<subset_type>                                ssg_doneset_type;
  typedef PlainArray<size_type>                               ssg_progress_vector_type;
  typedef PlainArray<const Symmetry*>                         ssg_symmetryptr_iterdata;
#else
#include <unordered_set>
#include "ContainerIO.hh"
  typedef std::set<subset_type>                               ssg_doneset_type;
  typedef std::vector<int>                                    ssg_progress_vector_type;
  typedef std::vector<const Symmetry*>                        ssg_symmetryptr_iterdata;
#endif

  class __ssg_output_object_base {
  public:
    inline virtual std::ostream& print_circuit(std::ostream& ost,
					       const size_type no,
					       const Circuit& circuit) const {
      return ost;
    }
    inline virtual std::ostream& print_cocircuit(std::ostream& ost,
						 const size_type no,
						 const Cocircuit& cocircuit) const {
      return ost;
    }
  };

  class __ssg_output_object : public __ssg_output_object_base {
  public:
    inline virtual std::ostream& print_circuit(std::ostream& ost,
					       const size_type no,
					       const Circuit& circuit) const {
      ost << message::lock << circuit << '\n' << message::unlock;
      return ost;
    }
    inline virtual std::ostream& print_cocircuit(std::ostream& ost,
						 const size_type no,
						 const Cocircuit& cocircuit) const {
      ost << message::lock << cocircuit << '\n' << message::unlock;
      return ost;
    }
  };

  class __ssg_output_object_debug : public __ssg_output_object_base {
  public:
    inline virtual std::ostream& print_circuit(std::ostream& ost,
					       const size_type no,
					       const Circuit& circuit) const {
      ost << message::lock << "C[" << no << "] := " << circuit << ";\n" << message::unlock;
      return ost;
    }
    inline virtual std::ostream& print_cocircuit(std::ostream& ost,
						 const size_type no,
						 const Cocircuit& cocircuit) const {
      ost << message::lock << "C[" << no << "] := " << cocircuit << ";\n" << message::unlock;
      return ost;
    }
  };
  
  template <ssg_mode_type mode>
  class SymmetricSubsetGraph {
  public:
    static constexpr int OUTPUT_FLUSH_FREQUENCY = 100000;
  private:
    class ObjectOutputter {
    public:
      
      // in order to switch on output of objects:
      typedef __ssg_output_object_base             output_object_base_type;
      typedef __ssg_output_object                  output_object_type;
      typedef __ssg_output_object_debug            output_object_debug_type;
    private:
      output_object_base_type* _action;
    public:
      inline ObjectOutputter() : _action(new output_object_base_type) {}
      inline ~ObjectOutputter() {
	if (_action) {
	  delete _action;
	}
      }
      inline virtual void activate() {
	if (_action) {
	  delete _action;
	}
	_action = new output_object_type();
      }
      inline virtual void deactivate() {
	if (_action) {
	  delete _action;
	}
	_action = new output_object_base_type();
      }
      inline virtual void activate_debug() {
	if (_action) {
	  delete _action;
	}
	_action = new output_object_debug_type();
      }
      inline virtual std::ostream& print_circuit(std::ostream& ost,
						 size_type symcount,
						 const Circuit& circuit) const {
	return _action->print_circuit(ost, symcount, circuit);
      }
      inline virtual std::ostream& print_cocircuit(std::ostream& ost,
						   size_type symcount,
						   const Cocircuit& cocircuit) const {
	return _action->print_cocircuit(ost, symcount, cocircuit);
      }
    };
  public:

    // the following typedef is subject to change into a full-fledged node class:
    // typedef std::pair<LabelSet, NodeMatrixClass<mode> > old_node_type;
    // ... like the one here:
    typedef SymmetricSubsetGraphNode<mode>                node_type;
    typedef typename node_type::matrix_type               matrix_type;
  public:
    class SymmetryWorker {
    private:
      const int                   _workerID;
      const SymmetricSubsetGraph* _callerptr;
    private:
      SymmetryWorker();
    public:
      SymmetryWorker(const int, const SymmetricSubsetGraph&);
      void old_symmetry_class_incrementalcheck(const dependent_set_type&,
					       const parameter_type,
					       const parameter_type);
      void old_symmetry_class_fullcheck(const dependent_set_type&);
    };
    friend class SymmetricSubsetGraph::SymmetryWorker;
  private:
    const int                         _ID;
    const size_type                   _runID;
    const ConfData&                   _confdata;
    const Options&                    _options;
  public:
    const node_type*                  _root_nodeptr;

    // parameters determined by calling master:
    const size_type                   _node_budget;
    const size_type*                  _current_workbuffersizeptr;
    const bool*                       _interruptptr;
    bool                              _continue_work;
  private:

    // organizing output:
    ObjectOutputter                   _object_outputter;

    // variables holding results:
    mutable std::ostringstream        _output_stream;
    size_type                         _totalcount;
    size_type                         _symcount;
    size_type                         _nodecount;
    size_type                         _deadendcount;
    size_type                         _earlydeadendcount;

    // statistics - can be mutable:
    mutable size_type                 _seecount;
    
    // nodes that could not be explored inside
    // the budget; reference set and owned by caller:
    std::deque<node_type>&            _open_nodes;

    // stack for iterative implementation _dfs_iterative of _dfs:
    std::stack<std::pair<node_type, parameter_type> > _nodes_to_explore;
  
    // this is a global result set used to collect
    // found cocircuits used for the template parameter "cocircuits":
    // it is owned by the caller,
    // and must therefore be protected by a static mutex:
    ssg_doneset_type&                 _result_set;
    static std::mutex                 _result_set_mutex;
    ssg_progress_vector_type          _progress_vector;
  private:
    size_type                         _orbitsize;
    subset_type                       _equivalent_subset;
  private:

    // checkpointing:
    ScreenReport                      _screenreport;
    
  private:
    // multi-threading:
    int                               _no_of_threads;
    std::vector<std::thread>          _threads;

    // parallelize the check for minimality in orbit:
  
    // the following bool is shared among threads to signal
    // that the result in a thread has been obtained;
    // we can, however, get away without atomic<bool>
    // because all threads would only switch this
    // from false to true and never back;
    // so, race conditions are not an issue:
    mutable bool                    _is_old_symmetry_class;
    std::vector<SymmetryWorker>     _symmetry_workers;

  private:
    SymmetricSubsetGraph();
    SymmetricSubsetGraph(const SymmetricSubsetGraph&);
    SymmetricSubsetGraph& operator=(const SymmetricSubsetGraph&);
  public:

    // constructor:
    inline SymmetricSubsetGraph(const int,
				const size_type,
				const ConfData&,
				const Options&,
				const node_type*,
				std::deque<node_type>&,
				ssg_doneset_type&,
				const size_type,
				const size_type*,
				const bool*);

    // destructor:
    inline ~SymmetricSubsetGraph();

    // accessors:
    inline const int       ID()                                           const { return _ID; }
    inline const size_type runID()                                        const { return _runID; }
    inline const std::deque<node_type>& open_nodes()                      const { return _open_nodes; }

    // results:
    inline const size_type totalcount()                                   const { return _totalcount; }
    inline const size_type symcount()                                     const { return _symcount; }
    inline const size_type nodecount()                                    const { return _nodecount; }
    inline const size_type deadendcount()                                 const { return _deadendcount; }
    inline const size_type earlydeadendcount()                            const { return _earlydeadendcount; }

    // statistics:
    inline const size_type seecount()                                     const { return _seecount; }

    // flush results buffer:
    inline Message& output_results(Message&, bool = false) const;

    // reporting:
    inline Message& report_progress(Message&);

    // functionality:
    inline const bool old_symmetry_class_incrementalcheck(const subset_type&);
    inline const bool old_symmetry_class_fullcheck       (const subset_type&);
  private:

    // internal methods:
    inline void _init();

    // void _init_symmetries_table();
    inline void _init_symmetry_workers();
    inline void _process_node(const node_type&, const parameter_type);
    inline void _dfs_iterative(const node_type&, const parameter_type);
    inline void _dfs(const node_type&, const parameter_type);

    // internal helper functions:
    inline bool _semi_is_not_monotonically_extendable(const subset_type&,
						      const parameter_type,
						      const matrix_type&,
						      const parameter_type);
    inline void _update_continue_work                ();

    // stream output/input:
  };

  // first announce the upcoming template specializations in order to allow
  // the implementations to be ordered arbitrarily:
  template <>
  inline void SymmetricSubsetGraph<circuits>::_dfs(const node_type&, const parameter_type);
  template <>
  inline void SymmetricSubsetGraph<cocircuits_independent>::_dfs(const node_type&, const parameter_type);
  template <>
  inline void SymmetricSubsetGraph<cocircuits>::_dfs(const node_type&, const parameter_type);
  
  template <>
  inline bool SymmetricSubsetGraph<cocircuits>::_semi_is_not_monotonically_extendable(const subset_type&,
										      const parameter_type,
										      const matrix_type&,
										      const parameter_type);

  template <ssg_mode_type mode>
  std::mutex SymmetricSubsetGraph<mode>::_result_set_mutex;

  template <ssg_mode_type mode>
  inline SymmetricSubsetGraph<mode>::SymmetricSubsetGraph(const int                                           ID,
							  const size_type                                     runID,
							  const ConfData&                                     confdata,
							  const Options&                                      options,
							  const node_type*                                    root_nodeptr,
							  std::deque<node_type>&                              open_nodes,
							  ssg_doneset_type&                                   result_set,
							  const size_type                                     node_budget,
							  const size_type*                                    current_workbufferptr,
							  const bool*                                         interruptptr) :
    _ID(ID),
    _runID(runID),
    _confdata(confdata),
    _options(options),
    _root_nodeptr(root_nodeptr),
    _result_set(result_set),
    _node_budget(node_budget),
    _current_workbuffersizeptr(current_workbufferptr),
    _interruptptr(interruptptr),
    _continue_work(true),
    _object_outputter(),
    _output_stream(),
    _totalcount(0),
    _symcount(0),
    _nodecount(0),
    _deadendcount(0),
    _earlydeadendcount(0),
    _seecount(0),
    _open_nodes(open_nodes),
    _nodes_to_explore(),
    _progress_vector(),
    _orbitsize(0),
    _is_old_symmetry_class(false),
    _screenreport(),
    _no_of_threads(CommandlineOptions::no_of_threads()) {
    _init();
    ssg_progress_vector_type progress_vector(1UL, 0UL);
    parameter_type start(0);
    parameter_type depth(_root_nodeptr->subset().card());
    if (depth > 0) {
      _progress_vector.resize(depth - 1, -1); // set the nodes at fixed depths to -1
      const parameter_type min_elem(_root_nodeptr->subset().min_elem());
      start = _confdata.no() - min_elem;
    }
    if (CommandlineOptions::iterative_dfs()) {
      _dfs_iterative(*_root_nodeptr, start);
    }
    else {
      _dfs(*_root_nodeptr, start);
    }
  }

  template <ssg_mode_type mode>
  inline SymmetricSubsetGraph<mode>::~SymmetricSubsetGraph() {
    if (_options.output_subsets()) {
      output_results(MessageStreams::result(), true);
    }
  }

  // flush results buffer if requested:
  template <ssg_mode_type mode>
  inline Message& SymmetricSubsetGraph<mode>::output_results(Message& msg, const bool force) const {
    if (force || _output_stream.tellp() > OUTPUT_FLUSH_FREQUENCY) {
      msg << message::lock
	  << _output_stream.str()
	  << message::unlock;
      _output_stream.clear();
      _output_stream.str("");
    }
    return msg;
  }

  // reporting:
  template <ssg_mode_type mode>
  inline Message& SymmetricSubsetGraph<mode>::report_progress(Message& msg) {
    if (_screenreport.decide_report(this->_nodecount)) {
      _screenreport.execute_ext_worker_report(msg,
					      this->_ID,
					      this->_nodecount,
					      this->_symcount,
					      this->_totalcount,
					      this->_deadendcount,
					      this->_earlydeadendcount,
					      this->_progress_vector);
    }
    return msg;
  }

  template <ssg_mode_type mode>
  inline SymmetricSubsetGraph<mode>::SymmetryWorker::SymmetryWorker(const int workerID,
								    const SymmetricSubsetGraph<mode>& ssg) :
    _workerID(workerID),
    _callerptr(&ssg) {
  }

  template <ssg_mode_type mode>
  inline void SymmetricSubsetGraph<mode>::SymmetryWorker::old_symmetry_class_fullcheck(const subset_type& current_subset) {
    if (_callerptr->_is_old_symmetry_class) {
      
      // some other thread may have found that current_partial_triang is not new:
      return;
    }
    const css_symmetryptr_iterdata& relevant_symmetries(_callerptr->_confdata.classified_subset_symmetries().worker_symmetryptrs(_workerID));
    for (ssg_symmetryptr_iterdata::const_iterator iter = relevant_symmetries.begin();
	 iter != relevant_symmetries.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.colex_increases(current_subset)) {
	_callerptr->_is_old_symmetry_class = true;
	return;
      }
    } 
  }

  template <ssg_mode_type mode>
  inline void SymmetricSubsetGraph<mode>::SymmetryWorker::old_symmetry_class_incrementalcheck(const subset_type&   current_subset,
											      const parameter_type max_elem,
											      const parameter_type new_elem) {
    if (_callerptr->_is_old_symmetry_class) {
      // some other thread may have found that current_partial_triang is not new:
      return;
    }
    const css_symmetryptr_iterdata& relevant_symmetries(_callerptr->_confdata.classified_subset_symmetries().worker_relevant_symmetryptrs(_workerID, new_elem, max_elem));
    for (css_symmetryptr_iterdata::const_iterator iter = relevant_symmetries.begin();
	 iter != relevant_symmetries.end();
	 ++iter) {
      // some other thread may have found that current_subset is not new:
      if (_callerptr->_is_old_symmetry_class) {
	return;
      }
      const Symmetry& g(**iter);
      if (g.colex_increases(current_subset)) {
	_callerptr->_is_old_symmetry_class = true;
	return;
      }
    } 
  }

  template <ssg_mode_type mode>
  inline const bool SymmetricSubsetGraph<mode>::old_symmetry_class_incrementalcheck(const subset_type& current_subset) {
#ifdef CLASSIFIED_DEBUG
    //////////////////////////////////////////////////////////////////////////////
    static size_type cnt_overtaking(0);
    static size_type cnt_compressing(0);
    static size_type cnt_exhaustive(0);
    //////////////////////////////////////////////////////////////////////////////
#endif
  
    // in this function, we assume that the minimal element of current_subset was added last 
    // and that current_subset minus {min_elem} cannot be colex-increased:
    const parameter_type min_elem(*current_subset.begin());
    const parameter_type max_elem(current_subset.max_elem());
  
    // use a preprocessed structure to accelerate colex-increasing check:
    if (CommandlineOptions::use_classified_symmetries() && _confdata.classified_subset_symmetries().colex_increases_by_overtaking(current_subset, max_elem, min_elem)) {
#ifdef CLASSIFIED_DEBUG
      //////////////////////////////////////////////////////////////////////////////
      MessageStreams::debug() << message::lock
			      << "early success no " << std::setw(9) << ++cnt_overtaking << ": overtaking" << std::endl
			      << message::unlock;
      //////////////////////////////////////////////////////////////////////////////
#endif
      return true;
    }
    if (CommandlineOptions::use_classified_symmetries() && _confdata.classified_subset_symmetries().colex_increases_by_compressing(current_subset, max_elem, min_elem)) {
#ifdef CLASSIFIED_DEBUG
      //////////////////////////////////////////////////////////////////////////////
      MessageStreams::debug() << message::lock
			      << "early success no " << std::setw(9) << ++cnt_compressing << ": compressing" << std::endl
			      << message::unlock;
      //////////////////////////////////////////////////////////////////////////////
#endif
      return true;
    }
  
    if (CommandlineOptions::parallel_symmetries() && !CommandlineOptions::use_switch_tables()) {
      _threads.clear();
      _is_old_symmetry_class = false;
      for (int i = 0; i < _no_of_threads; ++i) {
	MessageStreams::debug() << message::lock
				<< "spawning thread " << i << " ..." << std::endl
				<< message::unlock;
	_threads.push_back(std::thread(&SymmetryWorker::old_symmetry_class_incrementalcheck,
				       &_symmetry_workers[i],
				       std::ref(current_subset),
				       std::ref(max_elem),
				       std::ref(min_elem)));
	MessageStreams::debug() << message::lock
				<< "... done" << std::endl
				<< message::unlock;
      }
      for (int i = 0; i < _no_of_threads; ++i) {
	_threads[i].join();
      }
      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()) {
      	if (_confdata.subset_switchtable().colex_increases(current_subset)) {
	  MessageStreams::debug() << message::lock
				  << "found a colex-increasing switch product." << std::endl
				  << message::unlock;
	  return true;
	}
	else {
	  return false;
	}
      }
      else if (CommandlineOptions::use_classified_symmetries()) {
	if (_confdata.classified_subset_symmetries().colex_increases_by_relevant_symmetries(current_subset, max_elem, min_elem)) {
#ifdef CLASSIFIED_DEBUG
	  //////////////////////////////////////////////////////////////////////////////
	  {
	    std::lock_guard<std::mutex> lock(IO_sync::mutex);
	    MessageStreams::debug() << message::lock
				    << "late success no " << std::setw(9) << ++cnt_exhaustive << ": exhaustive search on "
				    << 100 * (_confdata.classified_subset_symmetries().relevant_symmetryptrs(min_elem, max_elem).size()) / _confdata.symmetries().size() << "% of elements" << std::endl
				    << message::unlock;
	  }
	  //////////////////////////////////////////////////////////////////////////////
#endif
	  return true;
	}
	else {
#ifdef COMPUTATIONS_DEBUG
	  for (SymmetryGroup::const_iterator iter = _confdata.symmetries().begin();
	       iter != _confdata.symmetries().end();
	       ++iter) {
	    if (iter->colex_increases(current_subset)) {
	      std::lock_guard<std::mutex> lock(IO_sync::mutex);
	      MessageStreams::debug() << message::lock
				      << "contradicting result for subset " << current_subset << ":" << std::endl
				      << "symmetry " << *iter << " colex increases " << current_subset << std::endl
				      << "but is not contained in relevant symmetries: " << _confdata.classified_subset_symmetries().relevant_symmetryptrs(min_elem, max_elem) << std::endl
				      << "total min-elem increasing relevant symmetries: " << _confdata.classified_subset_symmetries().relevant_symmetryptrs(min_elem, _confdata.no() - 1) << std::endl
				      << message::unlock;
	      exit(1);
	    }
	  }
#endif
	  return false;
	}
      }
      else {
	for (SymmetryGroup::const_iterator iter = _confdata.symmetries().begin();
	     iter != _confdata.symmetries().end();
	     ++iter) {
	  if (iter->colex_increases(current_subset)) {
	    return true;
	  }
	}
	return false;
      }
    }
  }

  template <ssg_mode_type mode>
  inline const bool SymmetricSubsetGraph<mode>::old_symmetry_class_fullcheck(const subset_type& current_subset) {

    // in this function, we assume nothing about current_subset minus min_elem (needed for cocircuits):
    if (CommandlineOptions::parallel_symmetries() && !CommandlineOptions::use_switch_tables()) {
      _threads.clear();
      _is_old_symmetry_class = false;
      for (int i = 0; i < _no_of_threads; ++i) {
	MessageStreams::debug() << message::lock
				<< "spawning thread " << i << " ..." << std::endl
				<< message::unlock;
	_threads.push_back(std::thread(&SymmetryWorker::old_symmetry_class_fullcheck,
				       &_symmetry_workers[i],
				       std::ref(current_subset)));
	MessageStreams::debug() << message::lock
				<< "... done" << std::endl
				<< message::unlock;
      }
      for (int i = 0; i < _no_of_threads; ++i) {
	_threads[i].join();
      }
      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()) {
      
	// experimental - only in effect at this point:
	if (_confdata.subset_switchtable().colex_increases(current_subset)) {
	  MessageStreams::debug() << message::lock
				  << "found a colex-increasing switch product." << std::endl
				  << message::unlock;
	  return true;
	}
	else {
	  return false;
	}
      }
      else {
	for (symmetry_iterdata::const_iterator iter = _confdata.symmetries().begin();
	     iter != _confdata.symmetries().end();
	     ++iter) {
	  const Symmetry& g(*iter);
	  if (g.colex_increases(current_subset)) {
	    return true;
	  }
	}
	return false;
      }
    }
  }

  template <ssg_mode_type mode>
  inline void SymmetricSubsetGraph<mode>::_init() {
    if (_options.output_subsets()) {
      MessageStreams::debug() << message::lock
			      << "activating object output ..." << std::endl
			      << message::unlock;
      _object_outputter.activate();
      MessageStreams::debug() << message::lock
			      << "... done" << std::endl
			      << message::unlock;
    }
    if (CommandlineOptions::output_asy()) {
      Graphics::run_to_asy(_ID);
      Graphics::matrix_to_asy(_ID, _runID, _root_nodeptr->subset(), _root_nodeptr->matrix());
    }
    if (CommandlineOptions::parallel_symmetries()) {
      _init_symmetry_workers();
    }
  }

  template <ssg_mode_type mode>
  inline void SymmetricSubsetGraph<mode>::_init_symmetry_workers() {
    MessageStreams::debug() << message::lock
			    << "initializing symmetry workers ..." << std::endl
			    << message::unlock;
    for (int i = 0; i < _no_of_threads; ++i) {
      _symmetry_workers.push_back(SymmetryWorker(i, *this));
    }
    MessageStreams::debug() << message::lock
			    << "... done" << std::endl
			    << message::unlock;
  }

  // if the template parameter is "circuits", we enumerate circuits:
  template <>
  inline void SymmetricSubsetGraph<circuits>::_process_node(const node_type&     current_node,
							    const parameter_type start) {

    MessageStreams::debug() << message::lock
			    << "processing node " << this->_nodecount
			    << " corresponding to subset " << current_node.subset() << " by all possible one-element extensions:" << std::endl
			    << message::unlock;
    const size_type workload(_confdata.no() - start);
    size_type doneload(0UL);
    const parameter_type next_card = current_node.card() + 1;
    const parameter_type depth = next_card;
    MessageStreams::debug() << message::lock
			    << "upsizing progress vector to " << depth << " ..." << std::endl
			    << message::unlock;
    _progress_vector.resize(depth);
      
    // for asy output:
    size_type parent_node_ID = this->_nodecount;
      
    for (parameter_type cnt = start; cnt < _confdata.no(); ++cnt) {
      ++doneload;
      const parameter_type i = _confdata.no() - cnt - 1;
      _progress_vector.at(depth - 1) = (100 * doneload) / workload;
      ++this->_nodecount;
      subset_type next_subset(current_node.subset());
      next_subset += i;
	
      // for asy output:
      size_type child_node_ID = this->_nodecount;
	
      if (CommandlineOptions::output_asy()) {
	  
	// the matrix is not needed yet, and it takes time to compute,
	// thus, it is not yet computed;
	// if graphics output is wanted, the problem is probably small,
	// so that computing it here just for the graphics should not harm:
	matrix_type next_matrix_for_asy = current_node.matrix();
	next_matrix_for_asy.augment(_confdata.points().col(i));
	Graphics::matrix_to_asy(_ID, _runID, next_subset, next_matrix_for_asy);
	Graphics::arc_to_asy(_ID, _runID, parent_node_ID, child_node_ID);
      }
	
      report_progress(MessageStreams::verbose());
      MessageStreams::debug() << message::lock
			      << "processing point " << i << " ..." << std::endl
			      << message::unlock;
	
      // check for equivalent colex-greater subsets:
      bool is_new = true;
      critical_element_data_type new_critelem_data;
      size_type new_stabilizer_card;
      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_incrementalcheck(next_subset);
      }
      else {
	  
	// use the method of TOPCOM-1.0.1:
	is_new = current_node.child_is_colexmax(_confdata, i, &new_critelem_data, &new_stabilizer_card);
	_seecount += current_node.seecount();
	current_node.reset_seecount();
      }
	
      if (!is_new) {

	// for asy output:
	if (CommandlineOptions::output_asy()) {
	  Graphics::notnew_to_asy(_ID, _runID, child_node_ID);
	}	
	MessageStreams::debug() << message::lock
				<< next_subset << " was found already: continue" << std::endl
				<< message::unlock;
	continue;
      }
      else {
	MessageStreams::debug() << message::lock
				<< next_subset << " was not found yet: process" << std::endl
				<< message::unlock;
      }
      matrix_type next_matrix(current_node.matrix());
      next_matrix.augment(_confdata.points().col(i));
      if (CommandlineOptions::debug()) {
	MessageStreams::debug() << message::lock;
	Matrix raw_matrix(current_node.matrix());
	raw_matrix.augment(_confdata.points().col(i));
	MessageStreams::debug() << "matrix with new column:" << std::endl;
	raw_matrix.pretty_print(std::cerr);
	MessageStreams::debug() << std::endl;
	MessageStreams::debug() << "new strict staircase matrix:" << std::endl;
	next_matrix.pretty_print(std::cerr);
	MessageStreams::debug() << std::endl;
	MessageStreams::debug() << message::unlock;
      }
      if (next_matrix.has_full_rank()) {
	if (i == 0) {
	    
	  // no further element available for extending subset - we can stop here:
	    
	  // for asy output:
	  if (CommandlineOptions::output_asy()) {
	    Graphics::deadend_to_asy(_ID, _runID, child_node_ID);
	  }	
	  continue;
	}
	  
	_update_continue_work();
	node_type next_node(std::move(next_subset),
			    next_card,
			    std::move(next_matrix),
			    std::move(new_critelem_data),
			    new_stabilizer_card);
	if (_continue_work) {
	  if (CommandlineOptions::iterative_dfs()) {
	    MessageStreams::debug() << message::lock
				    << "adding node to stack with " << next_node.subset() << std::endl
				    << message::unlock;
	    _nodes_to_explore.push(std::move(std::pair<node_type, parameter_type>(next_node, cnt + 1)));
	  }
	  else {
	    MessageStreams::debug() << message::lock
				    << "recurse with " << next_node.subset() << std::endl
				    << message::unlock;
	    _dfs(next_node, cnt + 1);
	  }
	}
	else {
	  MessageStreams::debug() << message::lock
				  << "interrupting enumeration of worker " << _ID << " ..." << std::endl
				  << message::unlock;
	  _open_nodes.push_back(next_node);
	  MessageStreams::debug() << message::lock
				  << "... done" << std::endl
				  << message::unlock;
	}
      }
      else {
#ifdef SUPER_VERBOSE
	MessageStreams::debug() << message::lock
				<< "dependency found of corank " << _rank - depth << std::endl
				<< message::unlock;
#endif
	MessageStreams::debug() << message::lock
				<< "circuit on " << next_subset
				<< " found with support size at most " << depth << std::endl
				<< message::unlock;
	  
	// we find the coefficients of the dependency in the final column of the
	// transformation matrix associated with next:
	Circuit newcircuit;
	bool is_minimal = true;
	size_type j(0);
	for (LabelSet::const_iterator iter = next_subset.begin();
	     iter != next_subset.end();
	     ++iter) {
	    
	  // note that _next_subset indexes columns from right to left;
	  // thus, the first element of _next_subset has its transformation coefficient
	  // in the last row of the transformation column:
	  const Field& coefficient = next_matrix.transformation()(depth - j - 1, depth - 1);
	  ++j;
	  if (coefficient == FieldConstants::ZERO) {
	    is_minimal = false;
	    break;
	  }
	  if (coefficient > FieldConstants::ZERO) {
	    MessageStreams::debug() << message::lock
				    << "adding " << *iter << " to positive part" << std::endl
				    << message::unlock;
	    newcircuit.first += *iter;
	  }
	  else if (coefficient < FieldConstants::ZERO) {
	    MessageStreams::debug() << message::lock
				    << "adding " << *iter << " to negative part" << std::endl
				    << message::unlock;
	    newcircuit.second += *iter;
	  }
	}
	if (is_minimal) {
	    
	  // here we count one circuit corresponding to the minimal dependent set "newbasis":
	  MessageStreams::debug() << message::lock
				  << "found minimal dependent set " << next_subset << std::endl
				  << message::unlock;
	  if (!CommandlineOptions::skip_orbitcount()) {
	    if (CommandlineOptions::use_classified_symmetries() || CommandlineOptions::use_naive_symmetries()) {
	      _orbitsize = (_confdata.symmetries().size() + 1) / (_confdata.symmetries().stabilizer_card(next_subset) + 1);
	    }
	    else if (CommandlineOptions::use_switch_tables() ) {
	      _orbitsize = _confdata.subset_switchtable().orbit_size(next_subset);
	    }
	    else {
	      _orbitsize = (_confdata.symmetries().size() + 1) / (new_stabilizer_card + 1);
	    }
	  }
	  if (_options.save_subsets()) {
	    std::lock_guard<std::mutex> lock(_result_set_mutex);
	    _result_set.insert(next_subset);
	  }
	    
	  // for asy output:
	  if (CommandlineOptions::output_asy()) {
	    Graphics::solution_to_asy(_ID, _runID, child_node_ID);
	  }	
	    
	  _totalcount += _orbitsize;
	  ++_symcount;
	  _object_outputter.print_circuit(_output_stream, _symcount, newcircuit);
	  output_results(MessageStreams::result());
	}
	else {
	    
	  // for asy output:
	  if (CommandlineOptions::output_asy()) {
	    Graphics::deadend_to_asy(_ID, _runID, child_node_ID);
	  }	    
	  ++_deadendcount;
	}
      }
    }
  }

  // the iterative version of dfs:
  template <>
  inline void SymmetricSubsetGraph<circuits>::_dfs_iterative(const node_type&     local_root,
							     const parameter_type start) {
    _nodes_to_explore.push(std::pair<node_type, parameter_type>(local_root, start));
    while (!_nodes_to_explore.empty()) {
      const std::pair<node_type, parameter_type> current_pair = std::move(_nodes_to_explore.top());
      _nodes_to_explore.pop();
      _process_node(current_pair.first, current_pair.second);
    }
  }
  
  // the recursive version of dfs:
  template <>
  inline void SymmetricSubsetGraph<circuits>::_dfs(const node_type&     current_node,
						   const parameter_type start) {
    _process_node(current_node, start);  
  }
  
  // if the template parameter is "cocircuits_independent", we enumerate cocircuits by hyperplane-generating bases:
  template <>
  inline void SymmetricSubsetGraph<cocircuits_independent>::_dfs(const node_type&     current_node,
								 const parameter_type start) {
    
    MessageStreams::debug() << message::lock
			    << "processing node " << this->_nodecount
			    << " corresponding to subset " << current_node.subset() << " by all possible one-element extensions:" << std::endl
			    << message::unlock;
    const size_type workload(_confdata.no() - start);
    size_type doneload(0UL);
    const parameter_type next_card = current_node.card() + 1;
    const parameter_type depth = next_card;
    MessageStreams::debug() << message::lock
			    << "upsizing progress vector to " << depth << " ..." << std::endl
			    << message::unlock;
    
    // for asy output:
    size_type parent_node_ID = this->_nodecount;
    _progress_vector.resize(depth);

    // we traverse the possible extensions from right to left because
    // right-to-left lexorder is compatible with the
    // bit representations of LabelSets, while the left-to-right
    // lexorder is not:
    for (parameter_type cnt = start; cnt < _confdata.no() - (_confdata.rank() - depth - 1); ++cnt) {
      ++doneload;
      const parameter_type i = _confdata.no() - cnt - 1;
      _progress_vector.at(depth - 1) = (100 * doneload) / workload;
      ++this->_nodecount;
      subset_type next_subset(current_node.subset() + i);

      // for asy output:
      size_type child_node_ID = this->_nodecount;

      if (CommandlineOptions::output_asy()) {

	// the matrix is not needed yet, and it takes time to compute,
	// thus, it is not yet computed;
	// if graphics output is wanted, the problem is probably small,
	// so that computing it here just for the graphics should not harm:
	matrix_type next_matrix_for_asy = current_node.matrix();
	next_matrix_for_asy.augment(_confdata.points().col(i));
	Graphics::matrix_to_asy(_ID, _runID, next_subset, next_matrix_for_asy);
	Graphics::arc_to_asy(_ID, _runID, parent_node_ID, child_node_ID);
      }
      
      report_progress(MessageStreams::verbose());

      // check for non-full rank first because this is mostly faster than the symmetry check:
      matrix_type next_matrix(current_node.matrix());
      next_matrix.augment(_confdata.points().col(i));
      MessageStreams::debug() << message::lock;
      Matrix raw_matrix(current_node.matrix());
      raw_matrix.augment(_confdata.points().col(i));
      MessageStreams::debug() << "matrix with new column:" << std::endl;
      raw_matrix.pretty_print(MessageStreams::debug());
      MessageStreams::debug() << std::endl;
      MessageStreams::debug() << "new strict staircase matrix:" << std::endl;
      next_matrix.pretty_print(MessageStreams::debug());
      MessageStreams::debug() << std::endl;
      MessageStreams::debug() << message::unlock;
      if (!next_matrix.has_full_rank()) {
	// cannot become a minimal spanning zero set of a cocircuit:
	MessageStreams::debug() << next_subset << " not independent - deadend" << std::endl;
	
	// for asy output:
	if (CommandlineOptions::output_asy()) {
	  Graphics::earlydeadend_to_asy(_ID, _runID, child_node_ID);
	}
	
	++_earlydeadendcount;
	continue;
      }

      // check for equivalent colex-greater subsets:
      bool is_new = true;
      critical_element_data_type new_critelem_data;
      size_type new_stabilizer_card;
      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_incrementalcheck(next_subset);
      }
      else {
	// use the method of TOPCOM-1.0.1:
	is_new = current_node.child_is_colexmax(_confdata, i, &new_critelem_data, &new_stabilizer_card);
	_seecount += current_node.seecount();
	current_node.reset_seecount();
      }

      if (!is_new) {

	// for asy output:
	if (CommandlineOptions::output_asy()) {
	  Graphics::notnew_to_asy(_ID, _runID, child_node_ID);
	}	
	MessageStreams::debug() << message::lock
				<< next_subset << " was found already: continue" << std::endl
				<< message::unlock;
	continue;
      }
      else {
	MessageStreams::debug() << message::lock
				<< next_subset << " was not found already: process" << std::endl
				<< message::unlock;
      }

      if (depth < _confdata.rank() - 1) {

	_update_continue_work();
	node_type next_node(std::move(next_subset),
			    next_card,
			    std::move(next_matrix),
			    std::move(new_critelem_data),
			    new_stabilizer_card);
	if (_continue_work) {
	  MessageStreams::debug() << message::lock
				  << "entering recursion with " << next_subset << std::endl
				  << message::unlock;
	  _dfs(next_node, cnt + 1);
	}
	else {
	  MessageStreams::debug() << message::lock
				  << "interrupting enumeration of worker " << _ID << " ..." << std::endl
				  << message::unlock;
	  _open_nodes.emplace_back(std::move(next_node));
	  MessageStreams::debug() << message::lock
				  << "... done" << std::endl
				  << message::unlock;
	}
      }
      else {
	// the set newbasis is independent and spans a hyperplane:
	MessageStreams::debug() << message::lock
				<< next_subset << " is spanning a hyperplane" << '\n'
				<< "cocircuit found with zeroset size at least " << next_subset.card()
				<< " in recursion depth " << depth << std::endl
				<< message::unlock;

	// Cocircuit checkcocircuit(Chirotope(*_pointsptr, false), next_subset);
	// we find the coefficients of the dependency in the final column of the
	// transformation matrix associated with next:
	Cocircuit newcocircuit;
	bool is_degenerate = false;
	subset_type result_subset(next_subset);
	MessageStreams::debug() << message::lock
				<< "computing signature ..." << std::endl
				<< message::unlock;
	for (size_type col = 0; col < _confdata.no(); ++col) {
	  if (result_subset.contains(col)) {
	    continue;
	  }
	  const Field coefficient = next_matrix.valuation(_confdata.points().col(col));
	  if (coefficient == FieldConstants::ZERO) {
	    // more points lie on the hyperplane spanned by newbasis:
	    is_degenerate = true;
	    result_subset += col;
	  }
	  if (coefficient > FieldConstants::ZERO) {
	    newcocircuit.first += col;
	  }
	  else if (coefficient < FieldConstants::ZERO) {
	    newcocircuit.second += col;
	  }
	}
	MessageStreams::debug() << message::lock
				<< "... done: cocircuit = " << newcocircuit << std::endl
				<< result_subset << " now equals zero set of cocircuit" << std::endl
				<< message::unlock;
	// make a preliminary search in _result_set
	// in order to possibly save the expensive symmetry check:
	if (is_degenerate && (_result_set.find(result_subset) != _result_set.end())) {

	  // for asy output:
	  if (CommandlineOptions::output_asy()) {
	    Graphics::deadend_to_asy(_ID, _runID, child_node_ID);
	  }
	  ++_deadendcount;
	  continue;
	}
	if (is_degenerate && old_symmetry_class_fullcheck(result_subset)) {

	  // for asy output:
	  if (CommandlineOptions::output_asy()) {
	    Graphics::deadend_to_asy(_ID, _runID, child_node_ID);
	  }
	  ++_deadendcount;
	}
	else {
	  if (is_degenerate) {
	    
	    // unfortunately, in the degenerate case we need to lock this section
	    // because of the shared _result_set:
	    // repeat the search, since _result_set may have
	    // received new elements in the meantime:
	    std::lock_guard<std::mutex> lock(_result_set_mutex);
	    if (_result_set.find(result_subset) != _result_set.end()) {

	      // for asy output:
	      if (CommandlineOptions::output_asy()) {
		Graphics::deadend_to_asy(_ID, _runID, child_node_ID);
	      }
	      ++_deadendcount;
	      continue;
	    }
	    else {
	      _result_set.insert(result_subset);
	    }
	  }
	  
	  // for asy output:
	  if (CommandlineOptions::output_asy()) {
	    Graphics::solution_to_asy(_ID, _runID, child_node_ID);
	  }	

	  if (!CommandlineOptions::skip_orbitcount()) {
	    if (CommandlineOptions::use_classified_symmetries() || CommandlineOptions::use_naive_symmetries()) {
	      _totalcount += (_confdata.symmetries().size() + 1) / (_confdata.symmetries().stabilizer_card(next_subset) + 1);
	    }
	    else if (CommandlineOptions::use_switch_tables() ) {
	      _totalcount += _confdata.subset_switchtable().orbit_size(next_subset);
	    }
	    else {
	      _totalcount += (_confdata.symmetries().size() + 1) / (new_stabilizer_card + 1);
	    }
	  }
	  ++_symcount;
	  _object_outputter.print_cocircuit(_output_stream, _symcount, newcocircuit);
	  output_results(MessageStreams::result());
	}
      }
    }
  }
 
  // if the template parameter is "cocircuits", we enumerate cocircuits
  // without saving the found canonical representatives in result_set;
  // this requires to keep the complete zero-set of an affine subspace in the enumeration nodes:
  template <>
  inline void SymmetricSubsetGraph<cocircuits>::_process_node(const node_type&     current_node,
							      const parameter_type start) {
   
    static const bool check_maximality_early = false;
    static const bool check_subset_not_monotonically_extendable = !CommandlineOptions::no_extension_check();
    static const bool check_subset_not_monotonically_extendable_early = CommandlineOptions::extension_check_first();

    MessageStreams::debug() << message::lock
			    << "processing node " << this->_nodecount
			    << " corresponding to subset " << current_node.subset() << " by all possible one-element extensions:" << std::endl
			    << message::unlock;
    const parameter_type next_card = current_node.card() + 1;
    const parameter_type depth = next_card;
    const parameter_type current_rank = current_node.matrix().rank();
    
    // for asy output:
    size_type parent_node_ID = this->_nodecount;

    // if the current subset has corank 1,
    // it is a candidate for a cocircuit,
    // and we can check for maximality either right away
    // or later:
    if (check_maximality_early) {
      if (current_rank == _confdata.rank() - 1) {
      
	// the set current_subset spans a hyperplane, but is it maximal?
	MessageStreams::debug() << message::lock
				<< current_node.subset() << " is spanning a hyperplane" << '\n'
				<< "cocircuit found with zeroset size at least " << current_node.subset().card()
				<< " in recursion depth " << depth << std::endl
				<< message::unlock;
	
	// we find the signature of the cocircuit by the valuation form of the determinant
	// of the zero-set of the hyperplane spanned by current_node.subset():
	Cocircuit newcocircuit;
	bool is_maximal = true;
	MessageStreams::debug() << message::lock
				<< "computing signature ..." << std::endl
				<< message::unlock;
	for (size_type col = 0; col < _confdata.no(); ++col) {
	  if (current_node.subset().contains(col)) {
	    continue;
	  }
	  const Field value = current_node.matrix().valuation(_confdata.points().col(col));
	  if (value == FieldConstants::ZERO) {

	    // more points lie on the hyperplane spanned by newbasis:
	    is_maximal = false;
	    break;
	  }
	  if (value > FieldConstants::ZERO) {
	    newcocircuit.first += col;
	  }
	  else if (value < FieldConstants::ZERO) {
	    newcocircuit.second += col;
	  }
	}
	if (is_maximal) {

	  // no further points on the hyperplane, i.e., we have found a cocircuit:
	
	  // for asy output:
	  if (CommandlineOptions::output_asy()) {
	    Graphics::solution_to_asy(_ID, _runID, parent_node_ID);
	  }
	
	  if (!CommandlineOptions::skip_orbitcount()) {
	    if (CommandlineOptions::use_classified_symmetries() || CommandlineOptions::use_naive_symmetries()) {
	      _totalcount += (_confdata.symmetries().size() + 1) / (_confdata.symmetries().stabilizer_card(current_node.subset()) + 1);
	    }
	    else if (CommandlineOptions::use_switch_tables() ) {
	      _totalcount += _confdata.subset_switchtable().orbit_size(current_node.subset());
	    }
	    else {
	      _totalcount += (_confdata.symmetries().size() + 1) / (current_node.stabilizer_card() + 1);
	    }
	  }
	  ++_symcount;
	  _object_outputter.print_cocircuit(_output_stream, _symcount, newcocircuit);
	  output_results(MessageStreams::result());
	  return;
	}
      }
    }

    const size_type workload(_confdata.no() - start);
    size_type doneload(0UL);
    MessageStreams::debug() << message::lock
			    << "resizing progress vector to " << depth << " ..." << std::endl
			    << message::unlock;
    _progress_vector.resize(depth);

    // check for monotonical maximality:
    bool is_monotonically_maximal = true;

    // we traverse the possible extensions from right to left because
    // right-to-left lexorder is compatible with the
    // bit representations of LabelSets, while the left-to-right
    // lexorder is not;
    MessageStreams::debug() << message::lock;
    MessageStreams::debug() << "_rank        = " << _confdata.rank() << std::endl;
    MessageStreams::debug() << "current_rank = " << current_rank << std::endl;
    MessageStreams::debug() << "start        = " << start << std::endl;
    MessageStreams::debug() << "loop min     = " << start << std::endl;
    MessageStreams::debug() << "loop max     = " << _confdata.no() - (_confdata.rank() - current_rank - 1) << std::endl;
    MessageStreams::debug() << message::unlock;

    // after the addition of the next point, the following is the minimal
    // number of points that is still missing in a hyperplane-spanning set:
    parameter_type max_cnt = _confdata.no();
    if (current_rank + 2 < _confdata.rank()) {

      // rank is too small for spanning a hyperplane after the addition of one element;
      // leave room for a sufficiently large no of elements to the right
      // (substraction is ok even if parameter_type is unsigned,
      // since _no is larger than _rank and thus than any rank difference):
      max_cnt -= (_confdata.rank() - 2 - current_rank);
    }
    for (parameter_type cnt = start; cnt < max_cnt; ++cnt) {
      ++doneload;
      const parameter_type i = _confdata.no() - cnt - 1;
      _progress_vector.at(depth - 1) = (100 * doneload) / workload;
      ++this->_nodecount;
      subset_type next_subset(current_node.subset() + i);

      // for asy output:
      size_type child_node_ID = this->_nodecount;
      
      report_progress(MessageStreams::verbose());

      // generate the data for the new node:
      matrix_type next_matrix(current_node.matrix());
      next_matrix.augment(_confdata.points().col(i));
      const parameter_type next_rank = next_matrix.rank();
      if (CommandlineOptions::debug()) {
	MessageStreams::debug() << message::lock;
	Matrix raw_matrix(current_node.matrix());
	raw_matrix.augment(_confdata.points().col(i));
	MessageStreams::debug() << "matrix with new column:" << std::endl;
	raw_matrix.pretty_print(MessageStreams::debug());
	MessageStreams::debug() << std::endl;
	MessageStreams::debug() << "new strict staircase matrix:" << std::endl;
	next_matrix.pretty_print(std::cerr);
	MessageStreams::debug() << std::endl;
	MessageStreams::debug() << message::unlock;
      }

      if (CommandlineOptions::output_asy()) {

	Graphics::matrix_to_asy(_ID, _runID, next_subset, next_matrix);
	Graphics::arc_to_asy(_ID, _runID, parent_node_ID, child_node_ID);
      }

      // check whether the rank of the subset is too high for a hyperplane already:
      if (next_rank >= _confdata.rank()) {
	
	// for asy output:
	if (CommandlineOptions::output_asy()) {
	  Graphics::earlydeadend_to_asy(_ID, _runID, child_node_ID);
	}
	++_earlydeadendcount;
	continue;
      }
      else {

	// from now on we know that new_subset spans a proper affine subspace,
	// and the current subset is not monotonically maximal:
	is_monotonically_maximal = false;
      }
      if (check_subset_not_monotonically_extendable && check_subset_not_monotonically_extendable_early) {
	  
	// in this pruning step,
	// we check first whether we are in a node that cannot be monotonically
	// extended to the zero set of a cocircuit:
	if (SymmetricSubsetGraph<cocircuits>::_semi_is_not_monotonically_extendable(next_subset, i, next_matrix, next_rank)) {
	
	  // for asy output:
	  if (CommandlineOptions::output_asy()) {
	    Graphics::earlydeadend_to_asy(_ID, _runID, child_node_ID);
	  }
	  ++_earlydeadendcount;
	  continue;
	}
      }
      
      // check for equivalent colex-greater subsets:
      bool is_new = true;

      // generate a local version of critical element data
      // that is filled by a method of the current node:
      critical_element_data_type new_critelem_data;
      size_type new_stabilizer_card;
      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_incrementalcheck(next_subset);
      }
      else {
	// use the method of TOPCOM-1.0.1:
	is_new = current_node.child_is_colexmax(_confdata, i, &new_critelem_data, &new_stabilizer_card);
	_seecount += current_node.seecount();
	current_node.reset_seecount();
      }

      if (!is_new) {

	// for asy output:
	if (CommandlineOptions::output_asy()) {
	  Graphics::notnew_to_asy(_ID, _runID, child_node_ID);
	}	
	MessageStreams::debug() << message::lock
				<< next_subset << " was found already: continue" << std::endl
				<< message::unlock;
	continue;
      }
      else {
	MessageStreams::debug() << message::lock
				<< next_subset << " was not found already: process" << std::endl
				<< message::unlock;
      }

      if (next_rank == current_rank) {
	
	// the new point is in the affine subspace spanned by the previous points
	// and is therefore contained in all hyperplanes that contain the
	// previous points;
	// thus, it must be added to the zero set of the cocircuit,
	// and we can break the traversal through all other additional points:

	// for asy output:
	if (CommandlineOptions::output_asy()) {
	  Graphics::veryearlydeadend_to_asy(_ID, _runID, child_node_ID);
	}
	MessageStreams::debug() << message::lock
				<< next_subset << " spans the same affine subspace as "
				<< current_node.subset() << ": add, recurse, break" << std::endl
				<< message::unlock;
	if (_confdata.no() - cnt > _confdata.rank() - 1 - next_rank) {
	  node_type next_node(std::move(next_subset),
			      next_card,
			      std::move(next_matrix),
			      std::move(new_critelem_data),
			      new_stabilizer_card);
	  if (CommandlineOptions::iterative_dfs()) {
	    MessageStreams::debug() << message::lock
				    << "pushing " << next_node.subset() << " onto stack" << std::endl
				    << message::unlock;
	    _nodes_to_explore.push(std::move(std::pair<node_type, parameter_type>(next_node, cnt + 1)));
	  }
	  else {
	    MessageStreams::debug() << message::lock
				    << "entering recursion with " << next_node.subset() << std::endl
				    << message::unlock;
	    _dfs(next_node, cnt + 1);
	  }
	}
	break;
      }
      else if (next_rank < _confdata.rank()) {

	// here, the new point increases the rank, but not beyond corank 1:
	if (_confdata.no() - cnt > _confdata.rank() - 1 - next_rank) {
	  if (check_subset_not_monotonically_extendable && !check_subset_not_monotonically_extendable_early) {
	  
	    // in this pruning step,
	    // we check first whether we are in a node that cannot be monotonically
	    // extended to the zero set of a cocircuit:
	    if (SymmetricSubsetGraph<cocircuits>::_semi_is_not_monotonically_extendable(next_subset, i, next_matrix, next_rank)) {
	      
	      // for asy output:
	      if (CommandlineOptions::output_asy()) {
		Graphics::earlydeadend_to_asy(_ID, _runID, child_node_ID);
	      }
	      ++_earlydeadendcount;
	      continue;
	    }
	  }

	  _update_continue_work();
	  node_type next_node(std::move(next_subset),
			      next_card,
			      std::move(next_matrix),
			      std::move(new_critelem_data),
			      new_stabilizer_card);
	  if (_continue_work) {
	    if (CommandlineOptions::iterative_dfs()) {
	      MessageStreams::debug() << message::lock
				      << "pushing " << next_node.subset() << " onto stack" << std::endl
				      << message::unlock;
	      _nodes_to_explore.push(std::move(std::pair<node_type, parameter_type>(next_node, cnt + 1)));
	    }
	    else {
	      MessageStreams::debug() << message::lock
				      << "entering recursion with " << next_node.subset() << std::endl
				      << message::unlock;
	      _dfs(next_node, cnt + 1);
	    }
	  }
	  else {
	    MessageStreams::debug() << message::lock
				    << "interrupting enumeration of worker " << _ID << " ..." << std::endl
				    << message::unlock;
	    _open_nodes.push_back(next_node);
	    MessageStreams::debug() << message::lock
				    << "... done" << std::endl
				    << message::unlock;
	  }
	}
      }
    }

    // if the current subset could not be extended by any point
    // without exceeding the rank beyond _rank - 1,
    // then it is monotonically maximal; if it has corank 1,
    // it is a candidate for a cocircuit;
    // if it has not been checked in the beginning of this method,
    // we have to do it now:

    if (!check_maximality_early) {
      if (is_monotonically_maximal) {
	if (current_rank != _confdata.rank() - 1) {
	  
	  // for asy output:
	  if (CommandlineOptions::output_asy()) {
	    Graphics::deadend_to_asy(_ID, _runID, parent_node_ID);
	  }
	  ++_deadendcount;
	}
	else {
	  
	  // the set current_subset spans a hyperplane in a monotonically maximal way:
	  MessageStreams::debug() << message::lock
				  << current_node.subset() << " is spanning a hyperplane" << std::endl
				  << message::unlock;
	  MessageStreams::debug() << message::lock
				  << "cocircuit found with zeroset size at least " << current_node.subset().card()
				  << " in recursion depth " << depth << std::endl
				  << message::unlock;
	  
	  // we find the signature of the cocircuit by the valuation form of the determinant
	  // of the zero-set of the hyperplane spanned by current_node.subset():
	  Cocircuit  newcocircuit;
	  bool is_maximal = true;
	  MessageStreams::debug() << message::lock
				  << "computing signature ..." << std::endl
				  << message::unlock;
	  for (size_type col = 0; col < _confdata.no(); ++col) {
	    if (current_node.subset().contains(col)) {
	      continue;
	    }
	    const Field value = current_node.matrix().valuation(_confdata.points().col(col));
	    if (value == FieldConstants::ZERO) {
	      
	      // more points lie on the hyperplane spanned by newbasis:
	      is_maximal = false;
	      break;
	    }
	    if (value > FieldConstants::ZERO) {
	      newcocircuit.first += col;
	    }
	    else if (value < FieldConstants::ZERO) {
	      newcocircuit.second += col;
	    }
	  }
	  if (is_maximal) {
	    
	    // no further points on the hyperplane, i.e., we have found a cocircuit:
	    MessageStreams::debug() << message::lock
				    << "cocircuit!" << std::endl
				    << message::unlock;
	    
	    // for asy output:
	    if (CommandlineOptions::output_asy()) {
	      Graphics::solution_to_asy(_ID, _runID, parent_node_ID);
	    }
	    
	    if (!CommandlineOptions::skip_orbitcount()) {
	      if (CommandlineOptions::use_classified_symmetries() || CommandlineOptions::use_naive_symmetries()) {
		_totalcount += (_confdata.symmetries().size() + 1) / (_confdata.symmetries().stabilizer_card(current_node.subset()) + 1);
	      }
	      else if (CommandlineOptions::use_switch_tables() ) {
		_totalcount += _confdata.subset_switchtable().orbit_size(current_node.subset());
	      }
	      else {
		_totalcount += (_confdata.symmetries().size() + 1) / (current_node.stabilizer_card() + 1);
	      }
	    }
	    ++_symcount;
	    _object_outputter.print_cocircuit(_output_stream, _symcount, newcocircuit);
	    output_results(MessageStreams::result());
	  }
	  else {
	    MessageStreams::debug() << message::lock
				    << "not maximal" << std::endl
				    << message::unlock;
	    
	    // for asy output:
	    if (CommandlineOptions::output_asy()) {
	      Graphics::deadend_to_asy(_ID, _runID, parent_node_ID);
	    }
	    ++_deadendcount;
	  }
	}
      }
    }
  }

  // the iterative version of dfs:
  template <>
  inline void SymmetricSubsetGraph<cocircuits>::_dfs_iterative(const node_type&     local_root,
							       const parameter_type start) {
    _nodes_to_explore.push(std::pair<node_type, parameter_type>(local_root, start));
    while (!_nodes_to_explore.empty()) {
      const std::pair<node_type, parameter_type> current_pair = std::move(_nodes_to_explore.top());
      _nodes_to_explore.pop();
      _process_node(current_pair.first, current_pair.second);
    }
  }

  // the recursive version of dfs:
  template <>
  inline void SymmetricSubsetGraph<cocircuits>::_dfs(const node_type&     current_node,
						     const parameter_type start) {
    _process_node(current_node, start);
  }

 
  template <>
  inline bool SymmetricSubsetGraph<cocircuits>::_semi_is_not_monotonically_extendable(const subset_type& next_subset,
										      const parameter_type next_min_elem,
										      const matrix_type& next_matrix,
										      const parameter_type next_rank) {
    if (next_rank > _confdata.rank() - 1) {
      return true;
    }
    else if (next_rank == _confdata.rank() - 1) {

      // the following checks whether the hyperplane spanned by next_subset has been
      // spanned by a colex-larger subset already
      // (special case of the check below requiring less memory allocations/deallocations):
      MessageStreams::debug() << message::lock
			      << "checking for containment in a known hyperplane spanned in earlier branch ..." << std::endl
			      << message::unlock;
      
      MessageStreams::debug() << message::lock
			      << "computing signature for non-elements already processed ..." << std::endl
			      << message::unlock;
      for (parameter_type col = next_min_elem + 1; col < _confdata.no(); ++col) {
	if (next_subset.contains(col)) {
	  continue;
	}
	const Field value = next_matrix.valuation(_confdata.points().col(col));
	if (value == FieldConstants::ZERO) {
	  
	  // earlier points lie on the hyperplane spanned by next_subset:
	  MessageStreams::debug() << message::lock
				  << "... found point of earlier branch on hyperplane - subset cannot be monotonically extendable" << std::endl
				  << message::unlock;
	  return true;
	}
      }
      MessageStreams::debug() << message::lock
			      << "... found no point of earlier branch on hyperplane - subset might be monotonically extendable" << std::endl
			      << message::unlock;
      return false;
    }
    else { // next_rank < _rank - 1
      
      // the following checks whether the affine space spanned by next_subset has been
      // spanned by a colex-larger subset already:
      MessageStreams::debug() << message::lock
			      << "checking for containment in a known affine subspace spanned in earlier branch ..." << std::endl
			      << message::unlock;
      
      MessageStreams::debug() << message::lock
			      << "computing rank increase for non-elements already processed ..." << std::endl
			      << message::unlock;
      for (parameter_type col = next_min_elem + 1; col < _confdata.no(); ++col) {
      	if (next_subset.contains(col)) {
      	  continue;
      	}
      	matrix_type check_rank_matrix = next_matrix;
      	check_rank_matrix.augment(_confdata.points().col(col));
      	if (check_rank_matrix.rank() == next_rank) {
	  
      	  // same affine subspace was discovered earlier;
      	  // thus, next_subset cannot be monotonically extended to a maximal subset:
	  MessageStreams::debug() << message::lock
				  << "... found earlier point in affine subspace - subset cannot be monotonically extendable" << std::endl
				  << message::unlock;
      	  return true;
      	}
      }
      MessageStreams::debug() << message::lock
			      << "... found no earlier point in affine subspace - subset might be monotonically extendable" << std::endl
			      << message::unlock;
      
      // this checks whether there is enough rank increase possible to the left of next_min_elem;      
      matrix_type rank_checker_matrix(next_matrix);
      for (parameter_type col = 0; col < next_min_elem; ++col) {
      	rank_checker_matrix.augment(_confdata.points().col(col));
	const parameter_type rank_checker_rank = rank_checker_matrix.rank();
	if (rank_checker_rank + next_min_elem - col < _confdata.rank() - 1) {

	  // rank (_rank - 1) is already unreachable:
	  MessageStreams::debug() << message::lock
				  << "not enough rank-increase left!" << std::endl
				  << message::unlock;
	  return true;
	}
      	if (rank_checker_rank >= _confdata.rank() - 1) {

	  // rank (_rank - 1) has been reached:
      	  return false;
      	}
      }

      // rank (_rank - 1) is unreachable with remaining columns:
      return true;
    }
  }

  template <ssg_mode_type mode>
  inline void SymmetricSubsetGraph<mode>::_update_continue_work() {
    // interrupt when
    // * a checkpoint has been triggered
    // or, depending on the choice of the user, 
    // * there are not enough nodes in the workbuffer
    // * the node budget has been used up:

    if (*this->_interruptptr || Signal::signal_received()) {
      _continue_work = false;
      return;
    }
    else {
      if (CommandlineOptions::parallel_enumeration() && CommandlineOptions::workbuffercontrol()) {
	    
	// break if not enough work on hold:
	if ((*this->_current_workbuffersizeptr < CommandlineOptions::min_workbuffersize())
	    || (CommandlineOptions::dump_status() && (this->_nodecount > _node_budget))) {
	  MessageStreams::debug() << message::lock
				  << "work buffer drained down to size " << *this->_current_workbuffersizeptr
				  << " - stopping enumeration with "
				  << _open_nodes.size()
				  << " unprocessed nodes" << std::endl
				  << message::unlock;
	  _continue_work = false;
	  return;
	}
      }
      else {
	    
	// enter the recursion only if there is still node budget left:
	if (this->_nodecount > _node_budget) {
	  MessageStreams::debug() << message::lock
				  << "node budget used up"
				  << " - stopping enumeration with "
				  << _open_nodes.size()
				  << " unprocessed nodes" << std::endl
				  << message::unlock;
	  _continue_work = false;
	  return;
	}
      }
    }
  }

}; // namespace topcom

#endif

// eof SymmetricSubsetGraph.hh
