////////////////////////////////////////////////////////////////////////////////
// 
// SymmetricExtensionGraphMaster.cc
//
//    produced: 22/03/2020 jr
// 
////////////////////////////////////////////////////////////////////////////////

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

#include "Message.hh"
#include "Statistics.hh"

#include "SymmetricExtensionGraphMaster.hh"

namespace topcom {

  const std::string SymmetricExtensionGraphMaster::_dump_startline = "TOPCOM SymmetricExtensionGraphMaster dump start:";
  const std::string SymmetricExtensionGraphMaster::_dump_endline   = "TOPCOM SymmetricExtensionGraphMaster dump end.";

  SymmetricExtensionGraphMaster::Worker::Worker(const int                            workerID,
						const SymmetricExtensionGraphMaster& segm) :
    _workerID(workerID),
    _callerptr(&segm),
    _root_nodeptr(0),
    _symcount(0UL),
    _totalcount(0UL),
    _nodecount(0UL),
    _deadendcount(0UL),
    _earlydeadendcount(0UL),
    _local_triangcache(),
    _mincard(segm._mincard),
    _mintriang(segm._mintriang),
    _seecount(0),
    _maxiter_coversimptighten(0),
    _no_of_runs(0),
    _open_nodes(),
    _state(idle) {
  }

  SymmetricExtensionGraphMaster::Worker::Worker(const Worker& sw) :
    _workerID(sw._workerID),
    _callerptr(sw._callerptr),
    _root_nodeptr(sw._root_nodeptr),
    _symcount(sw._symcount),
    _totalcount(sw._totalcount),
    _nodecount(sw._nodecount),
    _deadendcount(sw._deadendcount),
    _earlydeadendcount(sw._earlydeadendcount),
    _local_triangcache(sw._local_triangcache),
    _mincard(sw._mincard),
    _mintriang(sw._mintriang),
    _seecount(0),
    _maxiter_coversimptighten(0),
    _no_of_runs(sw._no_of_runs),
    _open_nodes(sw._open_nodes),
    _state(idle) {
  }

  void SymmetricExtensionGraphMaster::Worker::operator()() {
    
    // main loop of worker thread:
    MessageStreams::debug() << message::tab << "worker " << _workerID << " is spawned" << std::endl;

    // the thread_local caches must be initialized in each thread:
    SymmetricExtensionGraphNode::init_simpidx_cache();
    bool master_notified = false;
    while (!is_stopped()) {

      // debug information in each loop:
      MessageStreams::debug() << message::lock
			      << message::tab << "worker " << _workerID << " entering main loop with:" << std::endl;
      MessageStreams::debug() << message::tab << "\t_callerptr->_interrupt = " << _callerptr->_interrupt << std::endl;
      MessageStreams::debug() << message::tab << "\t_callerptr->_no_of_threads = " << _callerptr->_no_of_threads << std::endl;
      MessageStreams::debug() << message::tab << "\t_callerptr->_no_of_idle_threads = " << _callerptr->_no_of_idle_threads << std::endl;
      MessageStreams::debug() << message::tab << "\t_callerptr->_no_of_pending_results = " << _callerptr->_no_of_pending_results << std::endl;
      MessageStreams::debug() << message::tab << "\t_state = " << _state << std::endl;
      MessageStreams::debug() << message::tab << "\tmaster_notified = " << master_notified << std::endl
			      << message::unlock;

      // signal handling:
      if (Signal::signal_received()) {

	// give the master the opportunity to handle the signal:
	_callerptr->_main_condition.notify_one();
      }
      
      // the actual work is done here:
      if (is_hired()) {
	MessageStreams::debug() << message::lock
				<< message::tab << "worker " << _workerID << " doing work ..." << std::endl
				<< message::unlock;
	run();
	master_notified = false;
	MessageStreams::debug() << message::lock
				<< message::tab << "... worker " << _workerID << " done" << std::endl
				<< message::unlock;
      }
      else if (is_done() && !master_notified) {

	// actual work done:
	MessageStreams::debug() << message::lock
				<< message::tab << "... worker " << _workerID << " done - notifying master" << std::endl
				<< message::unlock;
	_callerptr->_main_condition.notify_one();
	master_notified = true;
      }

      // no work - wait:
      else if (is_idle()) {
	MessageStreams::debug() << message::lock
				<< message::tab << "worker " << _workerID << " idle - wait" << std::endl
				<< message::unlock;
	std::unique_lock<std::mutex> main_lock(_callerptr->_main_waitmutex);
	worker_condition.wait(main_lock, [this] { return wake_up(); });
	MessageStreams::debug() << message::lock
				<< message::tab << "worker " << _workerID << " waking up ..." << std::endl
				<< message::unlock;
      }
    }
    
    MessageStreams::debug() << message::lock
			    << message::tab << "worker " << _workerID << " terminating" << std::endl
			    << message::unlock;
  }

  void SymmetricExtensionGraphMaster::_collect_results() {
    if (!_exists_pending_result()) {
      return;
    }
    for (int i = 0; i < _no_of_threads; ++i) {
      if (_workers[i].is_done()) {
	_nodecount                += _workers[i].nodecount();
	_totalcount               += _workers[i].totalcount();
	_symcount                 += _workers[i].symcount();
	_deadendcount             += _workers[i].deadendcount();
	_earlydeadendcount        += _workers[i].earlydeadendcount();
	_triangcache.add(_workers[i].local_triangcache());
	_seecount                 += _workers[i].seecount();
	if (_workers[i].maxiter_coversimptighten() > _maxiter_coversimptighten) {
	  _maxiter_coversimptighten = _workers[i].maxiter_coversimptighten();
	}
#ifdef COMPUTATIONS_DEBUG
	MessageStreams::debug() << message::lock
				<< "... moving remaning work into queue ..." << std::endl
				<< message::unlock;
#endif
	std::move(_workers[i].open_nodes().begin(),
		  _workers[i].open_nodes().end(),
		  std::back_inserter<std::deque<node_type> >(_open_nodes));
#ifdef COMPUTATIONS_DEBUG
	MessageStreams::debug() << message::lock
				<< "... done" << std::endl
				<< message::unlock;
#endif
	_workers[i].clear_results();
      }
    }
    _current_workbuffersize = _open_nodes.size();
    update_node_budget();
    report_progress(MessageStreams::verbose());
  }

  void SymmetricExtensionGraphMaster::_run() {
    MessageStreams::verbose() << message::lock
			      << "starting enumeration ..." << std::endl
			      << message::unlock;
    if (CommandlineOptions::parallel_enumeration()) {
      
      // main loop of the master thread:
      while (
	     !_open_nodes.empty() // need to distribute work
	     || !_all_threads_idle() // need to wait for results
	     || _exists_pending_result() // need to collect results
	     ) {
	  
	// collect pending results of all done workers first:
	_collect_results();

	// check for termination by signal:
	if (Signal::signal_received()) {
	  trigger_checkpoint(true);
	  break;
	}
	
	// check for possible checkpoint if requested:
	if (CommandlineOptions::dump_status()) {
	  trigger_checkpoint(false);
	}
	  
	// check for more work and idle threads:
	if (_exists_idle_thread() && !_open_nodes.empty()) {
	    
	  // distribute work:
	  for (int i = 0; i < _no_of_threads; ++i) {
	    if (_workers[i].is_idle()) {
	      const node_type* next_nodeptr = new node_type(std::move(_open_nodes.front()));
	      _open_nodes.pop_front();
	      _workers[i].pass_work(next_nodeptr);
	    }
	    if (_open_nodes.empty()) {
	      break;
	    }
	  }
	    
	  // notify all workers with assigned work:
	  for (int i = 0; i < _no_of_threads; ++i) {
	    if (_workers[i].is_hired()) {
	      _workers[i].worker_condition.notify_one();
	    }
	  }
	}
	  
	// finally, wait if there is nothing to do:
	else {
	  MessageStreams::debug() << message::lock
				  << "master is waiting for results || work & idle_thread || termination ..." << std::endl
				  << message::unlock;
	  std::unique_lock<std::mutex> main_lock(_main_waitmutex);
	  _main_condition.wait(main_lock, [this] { return wake_up(); } );
	}
      }
      MessageStreams::debug() << message::lock
			      << "work completed - master is terminating itself" << std::endl
			      << message::unlock;
    }
    else {

      if (CommandlineOptions::dump_status()) {

	// here, the node budget controls the interrupt for checkpointing:
	_node_budget = CommandlineOptions::dump_frequency();
      }
      else {
	
	// infinite node budget - one instance of SymmetricExtensionGraph
	// does the whole enumeration in one go:
	_node_budget = std::numeric_limits<size_type>::max();
      }

      size_type cnt = 0;
      while (!_open_nodes.empty()) {
	const node_type next_node(std::move(_open_nodes.front()));
	SymmetricExtensionGraph seg(0,
				    cnt,
				    _symcount,
				    _confdata,
				    _options,
				    &next_node,
				    _triangcache,
				    _mincard,
				    _mintriang,
				    _open_nodes,
				    _node_budget,
				    &_current_workbuffersize,
				    &_interrupt);
	_nodecount                += seg.nodecount();
	_totalcount               += seg.totalcount();
	_symcount                 += seg.symcount();
	_deadendcount             += seg.deadendcount();
	_earlydeadendcount        += seg.earlydeadendcount();
	_triangcache.add(seg.local_triangcache());
	_seecount                 += seg.seecount();
	if (seg.maxiter_coversimptighten() > _maxiter_coversimptighten) {
	  _maxiter_coversimptighten = seg.maxiter_coversimptighten();
	}
	_open_nodes.pop_front();
	_current_workbuffersize = _open_nodes.size();
	
	// check for termination by signal:
	if (Signal::signal_received()) {
	  trigger_checkpoint(true);
	  break;
	}
	report_progress(MessageStreams::verbose());
	if (CommandlineOptions::dump_status()) {
	  trigger_checkpoint(false);
	}
	++cnt;
      }
    }
  }

  void SymmetricExtensionGraphMaster::_init() {

    // initialize the statistics file, if requested:
    if (CommandlineOptions::output_stats()) {
      Statistics::init();
    }
    
    // initialize the asymptote file, if requested:
    if (CommandlineOptions::output_asy()) {
      Graphics::init_asy(CommandlineOptions::asy_file());
      Graphics::typedef_for_partialtriang_to_asy();
      Graphics::header_to_asy(_confdata.no(), _confdata.rank());
      if (_confdata.rank() == 3) {
	Graphics::vertices_to_asy(_confdata.vertices());
	Graphics::points_to_asy(_confdata.points());
	Graphics::nodesize_for_pointsgraphics_to_asy();
      }
    }

    // by now, we have seen all simplices that might be occurring during the exploration:
    // SimplicialComplex::set_preprocessed(true);
    if (CommandlineOptions::parallel_enumeration()) {
      _no_of_threads = CommandlineOptions::no_of_threads();
      _init_workers();
    }
    else {

      // we need to generate a single worker for asymptote:
      if (CommandlineOptions::output_asy()) {
	Graphics::worker_to_asy();
      }
    }

    // from here on we need to block IO:
    MessageStreams::verbose() << message::lock
			      << "preparing an empty partial triangulation structure ..." << std::endl
			      << message::unlock;

    // the empty root triang is the starting point of the enumeration
    // but also the ground structure into which we may easily read
    // non-trivial partial triangulations; the advantage is that
    // the pointers to the global auxiliary data are already in place:
    const PartialTriang root_partial_triang(_confdata);
    const size_type root_card = root_partial_triang.card();
    MessageStreams::verbose() << message::lock
			      << "... done" << std::endl
			      << message::unlock;
    // delete facetsptr;
    
    // generate the root node
    if (!_confdata.has_simpidx_symmetries()) {
      
      // here, we use a representation of the symmetry group as a
      // permutation group on the vertices of simplicial complexes:
      MessageStreams::verbose() << message::lock
				<< "initializing root node using symmetries of points ..." << std::endl
				<< message::unlock;
      _rootptr = new node_type(_confdata, std::move(root_partial_triang), root_card);
      _seecount = _rootptr->seecount();
      _rootptr->reset_seecount();
      
      // initialize the simplex index tables according to command line options:
      SymmetricExtensionGraphNode::init_simpidx_table(_confdata, SimplicialComplex::no_of_simplices(_confdata.rank()));
      MessageStreams::verbose() << message::lock
				<< "... done" << std::endl
				<< message::unlock;
    }
    else {
      
      // here, we use a representation of the symmetry group as a
      // permutation group on simplex index sets representing simplicial complexes:
      MessageStreams::verbose() << message::lock
				<< "initializing root node using symmetries of simplex indices ..." << std::endl
				<< message::unlock;
      _rootptr = new node_type(_confdata, std::move(root_partial_triang), root_card);
      _seecount = _rootptr->seecount();
      _rootptr->reset_seecount();
      
      // initialize the simplex index tables according to command line options:
      SymmetricExtensionGraphNode::init_simpidx_table(_confdata, SimplicialComplex::no_of_simplices(_confdata.rank()));
      MessageStreams::verbose() << message::lock
				<< "... done" << std::endl
				<< message::unlock;
    }
    MessageStreams::debug() << message::lock;
    MessageStreams::debug() << "SymmetricExtensionGraphMaster:" << std::endl;
    MessageStreams::debug() << "root node         : " << *_rootptr << std::endl;
    MessageStreams::debug() << "freeintfacets     : " << _rootptr->partial_triang().freeintfacets() << std::endl;
    MessageStreams::debug() << "admissibles       : " << _rootptr->partial_triang().admissibles() << std::endl;
    MessageStreams::debug() << message::unlock;
    set_no_of_idle_threads(_no_of_threads);
    set_no_of_pending_results(0);
    if (CommandlineOptions::read_status()) {
      
      // here, we read an intermediate state from a TOPCOM dump and initialize
      // _open_node from there using the _rootptr as a template containing
      // all the necessary global information in the nodes:
      std::ifstream read_str(CommandlineOptions::read_file());
      read(read_str);      
      MessageStreams::verbose() << "SymmetricExtensionGraphMaster initialized from file " << CommandlineOptions::read_file() << std::endl;
      if (CommandlineOptions::debug()) {
	MessageStreams::debug() << "data:" 
				<< std::endl
				<< *this
				<< std::endl;
      }
      _checkpoint.init_oldcount(_nodecount);
      MessageStreams::verbose() << "checkpoint status:" << std::endl;
      MessageStreams::verbose().print_hseparator();
      report_progress(MessageStreams::verbose(), true);
      MessageStreams::verbose().print_hseparator();
    }
    else {
      ++_nodecount;
      _open_nodes.push_back(*_rootptr);
    }
    if (CommandlineOptions::output_stats()) {
      if (CommandlineOptions::use_switch_tables()) {
	Statistics::register_data(_seemstname, _confdata.extension_switchtable().seecount());
      }
      else {
	Statistics::register_data(_seecetname, _seecount);
      }
      Statistics::report();
      Statistics::reset();
    }
  }

  void SymmetricExtensionGraphMaster::_init_workers() {
    MessageStreams::verbose() << message::lock
			      << "init distributed workers ..." << std::endl
			      << message::unlock;
    for (int i = 0; i < _no_of_threads; ++i) {
      MessageStreams::verbose() << message::lock
				<< message::tab
				<< "initializing worker " << i << " ..." << std::endl
				<< message::unlock;
      _workers.emplace_back(i, *this);
      if (CommandlineOptions::output_asy()) {
	Graphics::worker_to_asy();
      }
      MessageStreams::verbose() << message::lock
				<< message::tab
				<< "... done" << std::endl
				<< message::unlock;
    }
    for (int i = 0; i < _no_of_threads; ++i) {
      MessageStreams::verbose() << message::lock
				<< message::tab
				<< "starting worker thread " << i 
				<< " ..." << std::endl
				<< message::unlock;
      _threads.push_back(std::thread(&SymmetricExtensionGraphMaster::Worker::operator(),
				     std::ref(_workers[i])));
      MessageStreams::verbose() << message::lock
				<< message::tab
				<< "... done" << std::endl
				<< message::unlock;
    }

    // from now on we need to block IO:
    MessageStreams::verbose() << message::lock
			      << "... done" << std::endl
			      << message::unlock;
  }

  void SymmetricExtensionGraphMaster::_term() {
#ifdef CONSTRUCTOR_DEBUG
    MessageStreams::debug() << message::lock
			    << "SymmetricExtensionGraphMaster::~SymmetricExtensionGraphMaster():" << '\n'
			    << "destructing owned pointers ..." << std::endl
			    << message::unlock;
#endif
    if (CommandlineOptions::parallel_enumeration()) {
      _term_workers();
    }

    // finish the asymptote file, if necessary:
    if (CommandlineOptions::output_asy()) {
      Graphics::nodepos_to_asy();
      Graphics::define_draw_node_for_partialtriang_to_asy();
      Graphics::drawings_to_asy();
      Graphics::term_asy();
    }

    // finish the statistics file, if necessary:
    if (CommandlineOptions::output_stats()) {
      if (CommandlineOptions::use_switch_tables()) {
	Statistics::change_data(_seemstname, _confdata.extension_switchtable().seecount());
      }
      else {
	Statistics::change_data(_seecetname, _seecount);
      }
      Statistics::report();
      Statistics::term();
    }

    // destroy all objects that have been generated by this class:
    if (_rootptr) {
      delete _rootptr;
    }
#ifdef CONSTRUCTOR_DEBUG
    MessageStreams::debug() << "... done" << std::endl;
#endif
  }

  void SymmetricExtensionGraphMaster::_term_workers() {
    MessageStreams::debug() << "work completed - master is stopping all workers ..." << std::endl;
    for (int i = 0; i < _no_of_threads; ++i) {
      _workers[i].stop_worker();
    }
    MessageStreams::debug() << "work completed - master is waking up all workers for termination ..." << std::endl;
    for (int i = 0; i < _no_of_threads; ++i) {
      _workers[i].worker_condition.notify_one();
    }
    MessageStreams::debug() << "... joining threads" << std::endl;
    for (int i = 0; i < _no_of_threads; ++i) {
      if (_threads[i].joinable()) {
	_threads[i].join();
      }
    }
  }

  // stream output/input:
  std::ostream& SymmetricExtensionGraphMaster::write(std::ostream& ost) const {
    ost << "_dump_no                  " << _checkpoint.dump_no() << std::endl;
    ost << "_totalcount               " << _totalcount << std::endl;
    ost << "_symcount                 " << _symcount << std::endl;
    ost << "_nodecount                " << _nodecount << std::endl;
    ost << "_deadendcount             " << _deadendcount << std::endl;
    ost << "_earlydeadendcount        " << _earlydeadendcount << std::endl;
    ost << "_seecount                 " << _seecount << std::endl;
    ost << "_mincard                  " << _mincard << std::endl;
    ost << "_mintriang                " << _mintriang << std::endl;
    ost << "_maxiter_coversimptighten " << _maxiter_coversimptighten << std::endl;
    ost << "_no                       " << _confdata.no() << std::endl;
    ost << "_rank                     " << _confdata.rank() << std::endl;
    if (_confdata.has_points()) {
      ost << "_points                   " << _confdata.points() << std::endl;
    }
    if (_confdata.has_chiro()) {
      ost << "_chiro                    " << _confdata.chiro() << std::endl;
    }
    if (_confdata.has_symmetries()) {
      ost << "_symmetries               " << _confdata.symmetries() << std::endl;
    }
    if (_confdata.has_required_symmetries()) {
      ost << "_required_symmetries      " << _confdata.required_symmetries() << std::endl;
    }
    ost << "_current_workbuffersize   " << _current_workbuffersize << std::endl;
    ost << "_open_nodes               " << _open_nodes << std::endl;
    return ost;
  }

  std::istream& SymmetricExtensionGraphMaster::read(std::istream& ist) {
    std::string dump_line;
    std::string last_dump_line;

    std::getline(ist, dump_line);

    // first, check if the standard-first-line was found to make sure the file is a TOPCOM dump:
    if (_dump_startline.compare(0, _dump_startline.length(), dump_line) != 0) {
      MessageStreams::forced() << "SymmetricExtensionGraphMaster::read(std::istream& ist): not a TOPCOM dump file - exiting" << std::endl;
      exit(1);
    }

    // next, check for data in key-value format:
    while ((std::getline(ist, dump_line))) {
      std::string::size_type lastPos = dump_line.find_first_not_of(" ", 0);
      std::string::size_type pos     = dump_line.find_first_of(" ", lastPos);
      std::string keyword            = dump_line.substr(lastPos, pos - lastPos);
	  
      // next, parse the partial computational result from the dump file:
      if (keyword == "_dump_no") {
	lastPos = dump_line.find_first_not_of(" ", pos);
	std::string value = dump_line.substr(lastPos, dump_line.length());      
	std::istringstream istrst (value, std::ios::in);      
	if (!(istrst >> _dump_no)) {
	  MessageStreams::forced() << "SymmetricExtensionGraphMaster::read(std::istream& ist): error while reading _dump_no - exiting" << std::endl;
	  exit(1);
	}
	MessageStreams::debug() << "_dump_no initialized with " << _dump_no << std::endl;
	_checkpoint.set_dump_no(_dump_no);
      }
      if (keyword == "_totalcount") {
	lastPos = dump_line.find_first_not_of(" ", pos);
	std::string value = dump_line.substr(lastPos, dump_line.length());      
	std::istringstream istrst (value, std::ios::in);      
	if (!(istrst >> _totalcount)) {
	  MessageStreams::forced() << "SymmetricExtensionGraphMaster::read(std::istream& ist): error while reading _totalcount - exiting" << std::endl;
	  exit(1);
	}
	MessageStreams::debug() << "_totalcount initialized with " << _totalcount << std::endl;
      }
      if (keyword == "_symcount") {
	lastPos = dump_line.find_first_not_of(" ", pos);
	std::string value = dump_line.substr(lastPos, dump_line.length());
	std::istringstream istrst (value, std::ios::in);
	if (!(istrst >> _symcount)) {
	  MessageStreams::forced() << "SymmetricExtensionGraphMaster::read(std::istream& ist): error while reading _symcount - exiting" << std::endl;
	  exit(1);
	}
	MessageStreams::debug() << "_symcount initialized with " << _symcount << std::endl;
      }
      if (keyword == "_nodecount") {
	lastPos = dump_line.find_first_not_of(" ", pos);
	std::string value = dump_line.substr(lastPos, dump_line.length());
	std::istringstream istrst (value, std::ios::in);
	if (!(istrst >> _nodecount)) {
	  MessageStreams::forced() << "SymmetricExtensionGraphMaster::read(std::istream& ist): error while reading _nodecount - exiting" << std::endl;
	  exit(1);
	}
	MessageStreams::debug() << "_nodecount initialized with " << _nodecount << std::endl;
      }
      if (keyword == "_deadendcount") {
	lastPos = dump_line.find_first_not_of(" ", pos);
	std::string value = dump_line.substr(lastPos, dump_line.length());
	std::istringstream istrst (value, std::ios::in);
	if (!(istrst >> _deadendcount)) {
	  MessageStreams::forced() << "SymmetricExtensionGraphMaster::read(std::istream& ist): error while reading _deadendcount - exiting" << std::endl;
	  exit(1);
	}
	MessageStreams::debug() << "_deadendcount initialized with " << _deadendcount << std::endl;
      }
      if (keyword == "_earlydeadendcount") {
	lastPos = dump_line.find_first_not_of(" ", pos);
	std::string value = dump_line.substr(lastPos, dump_line.length());
	std::istringstream istrst (value, std::ios::in);
	if (!(istrst >> _earlydeadendcount)) {
	  MessageStreams::forced() << "SymmetricExtensionGraphMaster::read(std::istream& ist): error while reading _earlydeadendcount - exiting" << std::endl;
	  exit(1);
	}
	MessageStreams::debug() << "_earlydeadendcount initialized with " << _earlydeadendcount << std::endl;
      }
      if (keyword == "_seecount") {
	lastPos = dump_line.find_first_not_of(" ", pos);
	std::string value = dump_line.substr(lastPos, dump_line.length());
	std::istringstream istrst (value, std::ios::in);
	if (!(istrst >> _seecount)) {
	  MessageStreams::forced() << "SymmetricExtensionGraphMaster::read(std::istream& ist): error while reading _seecount - exiting" << std::endl;
	  exit(1);
	}
	MessageStreams::debug() << "_seecount initialized with " << _seecount << std::endl;
      }
      if (keyword == "_mincard") {
	lastPos = dump_line.find_first_not_of(" ", pos);
	std::string value = dump_line.substr(lastPos, dump_line.length());
	std::istringstream istrst (value, std::ios::in);
	if (!(istrst >> _mincard)) {
	  MessageStreams::forced() << "SymmetricExtensionGraphMaster::read(std::istream& ist): error while reading _mincard - exiting" << std::endl;
	  exit(1);
	}
	MessageStreams::debug() << "_mincard initialized with " << _mincard << std::endl;
      }
      if (keyword == "_mintriang") {
	lastPos = dump_line.find_first_not_of(" ", pos);
	std::string value = dump_line.substr(lastPos, dump_line.length());
	std::istringstream istrst (value, std::ios::in);
	if (!(istrst >> _mintriang)) {
	  MessageStreams::forced() << "SymmetricExtensionGraphMaster::read(std::istream& ist): error while reading _mintriang - exiting" << std::endl;
	  exit(1);
	}
	MessageStreams::debug() << "_mintriang initialized with " << _mintriang << std::endl;
      }
      if (keyword == "_maxiter_coversimptighten") {
	lastPos = dump_line.find_first_not_of(" ", pos);
	std::string value = dump_line.substr(lastPos, dump_line.length());
	std::istringstream istrst (value, std::ios::in);
	if (!(istrst >> _maxiter_coversimptighten)) {
	  MessageStreams::forced() << "SymmetricExtensionGraphMaster::read(std::istream& ist): error while reading _maxiter_coversimptighten - exiting" << std::endl;
	  exit(1);
	}
	if (CommandlineOptions::debug()) {
	  MessageStreams::debug() << "_maxiter_coversimptighten initialized with " << _maxiter_coversimptighten << std::endl;
	}
      }

      // next, some data is parsed that makes sure that the dumped computational results were
      // obtained with the "right" data:
      if (keyword == "_no") {
	lastPos = dump_line.find_first_not_of(" ", pos);
	std::string value = dump_line.substr(lastPos, dump_line.length());
	std::istringstream istrst (value, std::ios::in);
	parameter_type no_check;
      
	if (!(istrst >> no_check)) {
	  MessageStreams::forced() << "SymmetricExtensionGraphMaster::read(std::istream& ist): error while reading _no - exiting" << std::endl;
	  exit(1);
	}
	if (_confdata.no() != no_check) {
	  MessageStreams::forced() << "SymmetricExtensionGraphMaster::read(std::istream& ist): no of points in input differs from no of points in dump file - exiting" << std::endl;
	  exit(1);
	}
	MessageStreams::debug() << "no of points in input coincides with no of points in dump file: okay" << std::endl;
      }
      if (keyword == "_rank") {
	lastPos = dump_line.find_first_not_of(" ", pos);
	std::string value = dump_line.substr(lastPos, dump_line.length());
	std::istringstream istrst (value, std::ios::in);
	parameter_type rank_check;

	if (!(istrst >> rank_check)) {
	  MessageStreams::forced() << "SymmetricExtensionGraphMaster::read(std::istream& ist): error while reading _rank - exiting" << std::endl;
	  exit(1);
	}
	if (_confdata.rank() != rank_check) {
	  MessageStreams::forced() << "SymmetricExtensionGraphMaster::read(std::istream& ist): rank of input differs from rank in dump file - exiting" << std::endl;
	  exit(1);
	}
	MessageStreams::debug() << "rank of input coincides with rank of dump file: okay" << std::endl;
      }
      if (_confdata.has_points()) {
	if (keyword == "_points") {
	  lastPos = dump_line.find_first_not_of(" ", pos);
	  std::string value = dump_line.substr(lastPos, dump_line.length());
	  std::istringstream istrst (value, std::ios::in);
	  PointConfiguration points_check;
	
	  if (!(istrst >> points_check)) {
	    MessageStreams::forced() << "SymmetricExtensionGraphMaster::read(std::istream& ist): error while reading _points - exiting" << std::endl;
	    exit(1);
	  }
	  if (_confdata.points() != points_check) {
	    MessageStreams::forced() << "SymmetricExtensionGraphMaster::read(std::istream& ist): points of input differ from points in dump file - exiting" << std::endl;
	    exit(1);
	  }
	  MessageStreams::debug() << "points of input coincide with points in dump file: okay" << std::endl;
	}
      }
      if (_confdata.has_chiro()) {
	if (keyword == "_chiro") {
	  lastPos = dump_line.find_first_not_of(" ", pos);
	  std::string value = dump_line.substr(lastPos, dump_line.length());
	  std::istringstream istrst (value, std::ios::in);
	  Chirotope chiro_check(_confdata.points(), false);
	
	  if (!(istrst >> chiro_check)) {
	    MessageStreams::forced() << "SymmetricExtensionGraphMaster::read(std::istream& ist): error while reading _chiro - exiting" << std::endl;
	    exit(1);
	  }
	  if (_confdata.chiro() != chiro_check) {
	    MessageStreams::forced() << "SymmetricExtensionGraphMaster::read(std::istream& ist): chirotope of input differs from chirotope in dump file - exiting" << std::endl;
	    exit(1);
	  }
	  MessageStreams::debug() << "chirotope of input coincides with chirotope of dump file: okay" << std::endl;
	}
      }
      if (keyword == "_symmetries") {
	lastPos = dump_line.find_first_not_of(" ", pos);
	std::string value = dump_line.substr(lastPos, dump_line.length());
	std::istringstream istrst (value, std::ios::in);
	SymmetryGroup symmetries_check(_confdata.no());

	// we expext the full symmetry group; thus, read as generators without computing closure:
	if (!(symmetries_check.read_complete_group(istrst))) {
	  MessageStreams::forced() << "SymmetricExtensionGraphMaster::read(std::istream& ist): error while reading _symmetries - exiting" << std::endl;
	  exit(1);
	}
	if (_confdata.symmetries() != symmetries_check) {
	  MessageStreams::forced() << "SymmetricExtensionGraphMaster::read(std::istream& ist): symmetries of input differ from symmetries in dump file - exiting" << std::endl;
	  exit(1);
	}
	MessageStreams::debug() << "symmetries of input coincide with _symmetries in dump file: okay" << std::endl;
      }
      if (keyword == "_requiredsymmetries") {
	lastPos = dump_line.find_first_not_of(" ", pos);
	std::string value = dump_line.substr(lastPos, dump_line.length());
	std::istringstream istrst (value, std::ios::in);
	SymmetryGroup required_symmetries_check(_confdata.no());
	if (!(istrst >> required_symmetries_check)) {
	  MessageStreams::forced() << "SymmetricExtensionGraphMaster::read(std::istream& ist): error while reading _required_symmetries - exiting" << std::endl;
	  exit(1);
	}
	if (_confdata.required_symmetries() != required_symmetries_check) {
	  MessageStreams::forced() << "SymmetricExtensionGraphMaster::read(std::istream& ist): required symmetries of input differ from required symmetries in dump file - exiting" << std::endl;
	  exit(1);
	}
	MessageStreams::debug() << "required symmetries of input coincide with _required_symmetries in dump file: okay" << std::endl;
      }

      // finally, read in the work buffer:
      if (keyword == "_current_workbuffersize") {
	lastPos = dump_line.find_first_not_of(" ", pos);
	std::string value = dump_line.substr(lastPos, dump_line.length());
	std::istringstream istrst (value, std::ios::in);
	if (!(istrst >> _current_workbuffersize)) {
	  MessageStreams::forced() << "SymmetricExtensionGraphMaster::read(std::istream& ist): error while reading _current_workbuffersize - exiting" << std::endl;
	  exit(1);
	}
	MessageStreams::debug() << "_current_workbuffersize initialized with " << _current_workbuffersize << std::endl;
      }
      if (keyword == "_open_nodes") {
	lastPos = dump_line.find_first_not_of(" ", pos);
	std::string value = dump_line.substr(lastPos, dump_line.length());
	std::istringstream istrst (value, std::ios::in);

	char c;

	// we read into an auxiliary structure because several pointers to
	// global data cannot be read but have to be constructed:
	std::deque<SymmetricExtensionGraphNodeReader> aux_reader;
	if (!(istrst >> aux_reader)) {
	  MessageStreams::forced() << "SymmetricExtensionGraphMaster::read(std::istream& ist): error while reading _open_nodes data into aux_reader - exiting"
				   << std::endl;
	  exit(1);
	}

	// next, we construct the actual nodes into _open_nodes from aux_reader
	// utilizing that this class knows the global data:
	_open_nodes.clear();
	for (std::deque<SymmetricExtensionGraphNodeReader>::iterator iter = aux_reader.begin();
	     iter != aux_reader.end();
	     ++iter) {
	  _open_nodes.emplace_back(this->_confdata, std::move(*iter));             // the data read into the aux reader
	}
      }
      last_dump_line = dump_line;
    }

    // finally, check last line for the standard end line in order
    // to make sure that the file is complete:
    if (_dump_endline.compare(0, _dump_endline.length(), last_dump_line) != 0) {
      MessageStreams::forced() << "last dump_line: " << last_dump_line << std::endl;
      MessageStreams::forced() << "SymmetricExtensionGraphMaster::read(std::istream& ist): TOPCOM dump file is incomplete - exiting" << std::endl;
      exit(1);
    }
    return ist;
  }
  
}; // namespace topcom

// eof SymmetricExtensionGraphMaster.cc
  
