////////////////////////////////////////////////////////////////////////////////
// 
// TriangFlips.cc
//
//    produced: 27/05/98 jr
// 
////////////////////////////////////////////////////////////////////////////////
#include <iostream>

#include <set>

#include "Flip.hh"
#include "TriangFlips.hh"

namespace topcom {

  // internal algorithms:

  void TriangFlips::_remove_destroyed_flips(const TriangNode& tn, 
					    const Flip& flip) {
    MarkedFlips::const_iterator iter = _flips.begin();
    while (iter != _flips.end()) {
#ifdef TOPCOM_FLIPS
      const FlipRep fliprep(iter->key());
#else
      const FlipRep fliprep((*iter).first);
#endif
      const Flip old_flip = Flip(tn, fliprep);
      if (!(old_flip.first * flip.first).empty()) {
	iter = _flips.erase(iter);
      }
      else {
	++iter;
      }
    }
  }

  void TriangFlips::_add_new_flips(const ConfData&             confdata,
				   const Options&              options,      
				   const TriangNode&           tn,
				   const SimplicialComplex&    restriction,
				   const symmetryptr_datapair& tn_symmetryptrs) {

    std::set<dependent_set_type> dependent_sets;
    std::set<Simplex>            done_set;

    MessageStreams::debug() << message::lock
			    << "adding flips in " << tn << " restricted to " << restriction << " ..." << std::endl
			    << message::unlock;
    const Simplex tn_support(tn.support());
    const Simplex groundset(Permutation(_no, _no));
    const Simplex missing_vertices(groundset - tn_support);

    // any new flip in tn must contain one of the flipped-in simplices:
    size_type countdown(restriction.card());
    for (SimplicialComplex::const_iterator iter1 = restriction.begin();
	 iter1 != restriction.end();
	 ++iter1) {
      --countdown;
      const Simplex& simp1(*iter1);
      MessageStreams::debug() << message::lock
			      << "processing flips containing " << simp1 << " ..." << std::endl
			      << message::unlock;
#ifdef TOPCOM_CONTAINERS
      if (done_set.contains(simp1, _rank)) {
#else
	if (done_set.find(simp1) != done_set.end()) {
#endif
	  // processed already:
	  continue;
#ifdef TOPCOM_CONTAINERS
	}
#else
      }
#endif
#ifdef TOPCOM_CONTAINERS
      done_set.insert(simp1, _rank);
#else
      done_set.insert(simp1);
#endif
      if (!options.lex_increasing_flips_only()
	  && !options.lex_decreasing_flips_only()
	  ) {

	// if lex-direction in one way or another
	// is no constraint, then all equivalent simplices
	// in tn can be stored as "done":
	for (symmetryptr_iterdata::const_iterator sym_iter = tn_symmetryptrs.first.begin();
	     sym_iter != tn_symmetryptrs.first.end();
	     ++sym_iter) {
	  const Symmetry& g(**sym_iter);
#ifdef TOPCOM_CONTAINERS
	  done_set.insert(g.map(simp1), _rank);
#else
	  done_set.insert(g.map(simp1));
#endif
	}
      }
      if (_no > 50) {
	MessageStreams::debug() << message::lock
				<< "... still " << countdown << " simplices to check for flips ..." << std::endl
				<< message::unlock;
      }
      
      // search for missing interior points:
      for (Simplex::const_iterator missing_iter = missing_vertices.begin();
	   missing_iter != missing_vertices.end();
	   ++missing_iter) {
	dependent_set_type dependent_set(simp1 + *missing_iter);
	MessageStreams::debug() << message::lock
				<< "dependent_set = " << dependent_set << std::endl
				<< message::unlock;
	size_type no_of_flips = _process_dependent_set(confdata,
						       options,
						       dependent_set,
						       tn,
						       tn_symmetryptrs,
						       dependent_sets,
						       done_set);
	if (options.one_flip_only() && (no_of_flips > 0)) {
	  return;
	}
      }	
      size_type count(0);
	
      // search for adjacent simplices:
      if (false && confdata.has_inctable()) {
	
	// we can use preprocessed incidences:
	const SimplicialComplex intfacets(confdata.inctable().intfacets(simp1));
	for (SimplicialComplex::const_iterator intiter = intfacets.begin();
	     intiter!= intfacets.end();
	     ++intiter) {
	  const SimplicialComplex simppair(tn * confdata.inctable().intcofacets(*intiter));
	  if (simppair.card() < 2) {
	    
	    // facet is not interior in tn but in conf - contradiction to triang property of tn:
	    std::cerr << "!!!! intfacet "
		      << *intiter
		      << " of "
		      << simp1
		      << " is not interior in triang "
		      << tn << std::endl;
	    continue;
	  }
	  dependent_set_type dependent_set(simppair.support());
	  MessageStreams::debug() << message::lock
				  << "dependent_set = " << dependent_set << std::endl
				  << message::unlock;
	  size_type no_of_flips = _process_dependent_set(confdata,
							 options,
							 dependent_set,
							 tn,
							 tn_symmetryptrs,
							 dependent_sets,
							 done_set);
	  if (options.one_flip_only() && (no_of_flips > 0)) {
	    return;
	  }
	  if (++count > _rank) {
	    
	    // we have all neighbors already:
	    break;
	  }   
	}
      }
      else {
	
	// here, we do not have the preprocessed incidences:
	for (SimplicialComplex::const_iterator iter2 = tn.begin();
	     iter2 != tn.end();
	     ++iter2) {
	  const Simplex& simp2(*iter2);
	  dependent_set_type dependent_set(simp1 + simp2);
	  if (dependent_set.card() != _rank + 1) {
	    continue;
	  }
	  MessageStreams::debug() << message::lock
				  << "\t processing adjacent simplex " << simp2 << " ..." << std::endl
				  << message::unlock;
	  size_type no_of_flips = _process_dependent_set(confdata,
							 options,
							 dependent_set,
							 tn,
							 tn_symmetryptrs,
							 dependent_sets,
							 done_set);
	  if (options.one_flip_only() && (no_of_flips > 0)) {
	    return;
	  }
	  if (++count > _rank) {
	    
	    // we have all neighbors already:
	    break;
	  }   
	}
      }
    }

    // report on flip-deficiency, if requested:
    if (!options.full_only()
	&&
	!options.lex_increasing_flips_only()
	&&
	!options.lex_decreasing_flips_only()
	&&
	!options.one_flip_only()
	&&
	CommandlineOptions::neighborcount() 
	&&
	(_flips.size() < _no - _rank)) {
      MessageStreams::debug() << message::lock
			      << "triangulation" << '\n'
			      << tn << std::endl
			      << message::unlock;
#ifdef TOPCOM_FLIPS
      MessageStreams::debug() << message::lock
			      << "has only " << _flips.size() << " flips." << std::endl
			      << message::unlock;
#else
      MessageStreams::debug() << message::lock
			      << "has only " << _flips.size() << " flips." << std::endl
			      << message::unlock;
#endif
    }
    MessageStreams::debug() << message::lock
			    << "... done." << std::endl
			    << message::unlock;
  }

  size_type TriangFlips::_process_dependent_set(const ConfData&               confdata,
						const Options&                options,
						const dependent_set_type&     dependent_set,
						const TriangNode&             tn,
						const symmetryptr_datapair&   tn_symmetryptrs,
						std::set<dependent_set_type>& dependent_sets,
						std::set<Simplex>&            done_set) {
      
    size_type count(0);
    if (dependent_sets.find(dependent_set) != dependent_sets.end()) {

      // dependent_set has been processed already:
      MessageStreams::debug() << message::lock
			      << "\t ... already processed - continue" << std::endl
			      << message::unlock;
      return count;
    }
	
    // try to build a flip from dependent_set:
    const FlipRep fliprep(confdata.chiro(), dependent_set, tn);
    MessageStreams::debug() << message::lock
			    << "\t ... induces flip supported on circuit " << fliprep << " ..." << std::endl
			    << message::unlock;
    bool invariantly_valid(true);
    bool symdependently_valid(true);
    if (fliprep) {

      // succeeded:
      if (options.full_only() && fliprep.kills_vertex()) {

	// not a valid flip:
	invariantly_valid = false;
      }
      else if (options.dont_add_points() && fliprep.adds_vertex()) {

	// not a valid flip:
	invariantly_valid = false;
      }
      if (options.lex_increasing_flips_only() && fliprep.is_nonlex()) {

	// not a valid flip:
	symdependently_valid = false;
      }
      else if (options.lex_decreasing_flips_only() && fliprep.is_nonrevlex()) {

	// not a valid flip:
	symdependently_valid = false;
      }
      if (invariantly_valid && symdependently_valid) {
      
	// insert flip:
	_flips[fliprep] = false;
	++count;
	if (options.one_flip_only()) {
	  return count;
	}
      }
      dependent_sets.insert(dependent_set);
      for (symmetryptr_iterdata::const_iterator sym_iter = tn_symmetryptrs.first.begin();
	   sym_iter != tn_symmetryptrs.first.end();
	   ++sym_iter) {
	const Symmetry& g(**sym_iter);
	const dependent_set_type equivalent_dependent_set(g.map(dependent_set));
	if (dependent_sets.find(equivalent_dependent_set) != dependent_sets.end()) {
	  
	  // equivalen_dependent_set has been processed already:
	  MessageStreams::debug() << message::lock
				  << "\t ... equivalent dependent set already processed, too - continue" << std::endl
				  << message::unlock;
	}
	else {
	  if (invariantly_valid) {
	    
	    // only in this case, check all equivalent flips for symmetry-dependent validity:
	    symdependently_valid = true;
	    FlipRep equivalent_fliprep(g.map(fliprep));
	    if (options.lex_increasing_flips_only() && equivalent_fliprep.is_nonlex()) {
	      
	      // not a valid flip:
	      symdependently_valid = false;
	    }
	    else if (options.lex_decreasing_flips_only() && equivalent_fliprep.is_nonrevlex()) {
	      
	      // not a valid flip:
	      symdependently_valid = false;
	    }
	    if (symdependently_valid) {
	      
	      // equivalent fliprep is also valid:
	      _flips[equivalent_fliprep] = false;
	      ++count;
	      if (options.one_flip_only()) {
		return count;
	      }
	    }
	  }
	}

	// save the processed equivalent dependent set as "done":
	dependent_sets.insert(equivalent_dependent_set);
      }
    }
    else {

      // save processed dependent set:
      dependent_sets.insert(dependent_set);
      for (symmetryptr_iterdata::const_iterator sym_iter = tn_symmetryptrs.first.begin();
	   sym_iter != tn_symmetryptrs.first.end();
	   ++sym_iter) {

	// save equivalent dependent set because it induces no flip either:
	const Symmetry& g(**sym_iter);
	const dependent_set_type equivalent_dependent_set(g.map(dependent_set));
	dependent_sets.insert(equivalent_dependent_set);
      }
    }
    return count;
  }



}; // namespace topcom
  
// eof TriangFlips.cc
