////////////////////////////////////////////////////////////////////////////////
// 
// ComputeTriangs.cc
//
//    produced: 25 Nov 1999 jr
// 
////////////////////////////////////////////////////////////////////////////////

#include "Signal.hh"
#include "CommandlineOptions.hh"
#include "Cache.hh"

#include "Permutation.hh"
#include "Volumes.hh"
#include "Circuits.hh"
#include "Facets.hh"
#include "Admissibles.hh"
#include "InteriorFacets.hh"
#include "PartialTriang.hh"
#include "CheckTriang.hh"
#include "CheckSymmetries.hh"
#include "Flip.hh"
#include "MarkedFlips.hh"
#include "TriangNode.hh"
#include "TriangFlips.hh"
#include "FineTriang.hh"
#include "SymmetricFlipGraph.hh"
#include "SymmetricFlipGraphAlgorithms.hh"
#include "SymmetricExtensionGraphMaster.hh"

#include "ComputeTriangs.hh"

namespace topcom {
  
  Message& ComputeTriangs::write_header(Message& msg) const {
    msg << std::endl;
    msg << "------------------------------------------------------------------\n";
    msg << " computing triangulations of a point configuration up to symmetry \n";
    msg << " TOPCOM client: " << CommandlineOptions::client() << '\n';
    msg << "------------------------------------------------------------------\n";
    msg << std::endl;
    return msg;
  }

  std::istream& ComputeTriangs::read_input(std::istream& ist) {
    _confdata.read_input(ist);
    return ist;
  }

  void ComputeTriangs::preprocess_data() {
    _confdata.make_chiro();
    _confdata.make_trivial_symmetries(); // it comes in handy to have a trivial group available
    _confdata.make_valid_symmetries();
    if (_options.compute_all()) {
      _confdata.make_admtable();
      _confdata.make_facets();
      _confdata.make_inctable();
    }
    else {
      _confdata.make_seed();
      _confdata.make_seed_symmetryptrs();
      if (CommandlineOptions::ignore_symmetries()) {
	_confdata.make_trivial_points_simpidx_symmetries();
      }
      else {
	_confdata.make_valid_points_simpidx_symmetries();
      }
    }
    if (CommandlineOptions::regularity_checks_needed()) {
      _confdata.make_inctable();
    }
    if (CommandlineOptions::check_nonsubregular()
	|| CommandlineOptions::check_nonsupregular()
	|| CommandlineOptions::check_nonregular_comp()
	|| CommandlineOptions::check_possibly_nonregular_comp()
	|| CommandlineOptions::check_findregular()
	|| CommandlineOptions::check_randomfindregular()
	) {
      _confdata.make_seed();
      _confdata.make_placingtriang();
    }
    if (CommandlineOptions::points_simpidx_syms_needed()) {
      _confdata.make_points_simpidx_symmetries(); // whenever a flip-graph exploration is planned (direct or downstream)
      _confdata.make_valid_points_simpidx_symmetries(); // depending on options, valid symmetry pairs are needed
      _confdata.make_trivial_points_simpidx_symmetries(); // it comes in handy to have a trivial group available
    }
    if (CommandlineOptions::check_unimodular()
	|| CommandlineOptions::use_volume_order()
	|| CommandlineOptions::use_volumes()
	|| CommandlineOptions::use_gkz()
	|| CommandlineOptions::output_gkzvecs()) {
      _confdata.make_voltable();
      _confdata.make_volume();
    }
    if (CommandlineOptions::output_asy()
	&& (_confdata.rank() == 3)) {
      _confdata.make_vertices();
    }
    if (CommandlineOptions::simpidx_symmetries()
	&& _options.compute_all()) {
      _confdata.make_simpidx_symmetries();
    }
    if (CommandlineOptions::use_classified_symmetries()) {
      _confdata.make_classified_extension_symmetries();
    }
    if (CommandlineOptions::use_switch_tables()) {
      _confdata.make_extension_switchtable();
    }
  }
  
  const std::pair<size_type, SimplicialComplex> ComputeTriangs::findmin_by_extension() {
    SymmetricExtensionGraphMaster segm(_confdata, _options);
    MessageStreams::verbose() << segm.nodecount() << " partial triangulations visited in total." << std::endl;
    MessageStreams::verbose() << segm.deadendcount() << " branching deadends." << std::endl;
    MessageStreams::verbose() << segm.earlydeadendcount() << " early detected deadends." << std::endl;
    MessageStreams::verbose() << segm.mintriang() << " is a minimal triangulation." <<  std::endl;
    MessageStreams::verbose() << segm.mincard() << " simplices in a minimal triangulation." << std::endl;
    return std::pair<size_type, SimplicialComplex>(segm.mincard(), segm.mintriang());
  }

  const size_type ComputeTriangs::enumerate_by_extension() {
    SymmetricExtensionGraphMaster segm(_confdata, _options);
    if (Signal::signal_received()) {
      MessageStreams::verbose().print_dumpseparator();
      MessageStreams::verbose() << "### intermediate results forced by signal:" << std::endl;
      MessageStreams::verbose().print_dumpseparator();
    }
    MessageStreams::verbose() << segm.nodecount() << " partial triangulations visited in total." << std::endl;
    MessageStreams::verbose() << segm.deadendcount() << " branching deadends." << std::endl;
    MessageStreams::verbose() << segm.earlydeadendcount() << " early detected deadends." << std::endl;
    MessageStreams::verbose() << segm.maxiter_coversimptighten() << " max iterations in coversimp tightening." << std::endl;
    MessageStreams::verbose() << segm.symcount() << " symmetry classes";
    if (!CommandlineOptions::skip_orbitcount()) {
      MessageStreams::verbose() << " --- " << segm.totalcount() << " triangulations in total";
    }
    MessageStreams::verbose() << '.' << std::endl;
    MessageStreams::verbose() << segm.workbuffersize() << " open nodes left to process";
    MessageStreams::verbose() << "." << std::endl;
    return segm.symcount();
  }

  const size_type ComputeTriangs::enumerate_by_flips() {
    SymmetricFlipGraph sfg(0, _confdata, _options);
    MessageStreams::verbose() << sfg.symcount() << " symmetry classes";
    if (!CommandlineOptions::skip_orbitcount()) {
      MessageStreams::verbose() << " --- " << sfg.totalcount() << " triangulations in total";
    }
    MessageStreams::verbose() << "." << std::endl;
    return sfg.symcount();
  }

  const bool ComputeTriangs::seed_is_nonsubregular() {
    if (RegularityCheck(_confdata, _confdata.seed()).is_regular()) {
      return false;
    }
    SymmetricFlipGraphAlgorithms sfg_algs(0,
					  _confdata,
					  _options,
					  _global_triangcache,
					  _local_triangcache);

    // the following function used a copy of options to use only GKZ-increasing flips:
    bool regulars_upflip_reachable = sfg_algs.find_regular_by_gkz_increasing_flips(_confdata.seed(), DIR_UP);
    return !regulars_upflip_reachable;
  }

  const bool ComputeTriangs::seed_is_nonsupregular() {
    if (RegularityCheck(_confdata, _confdata.seed()).is_regular()) {
      return false;
    }
    SymmetricFlipGraphAlgorithms sfg_algs(0,
					  _confdata,
					  _options,
					  _global_triangcache,
					  _local_triangcache);

    // the following function used a copy of options to use only GKZ-increasing flips:
    bool regulars_downflip_reachable = sfg_algs.find_regular_by_gkz_increasing_flips(_confdata.seed(), DIR_DOWN);
    return !regulars_downflip_reachable;
  }

  const bool ComputeTriangs::seed_is_nonregularcomp() {
    if (_confdata.has_points()) {
      if (RegularityCheck(_confdata, _confdata.seed()).is_regular()) {
	return false;
      }
    }
    SymmetricFlipGraphAlgorithms sfg_algs(0,
					  _confdata,
					  _options,
					  _global_triangcache,
					  _local_triangcache);

    // the following function used a various sets of options to
    // find "no" fast by heuristics and eventually decide by BFS:
    bool regulars_reachable = sfg_algs.find_regular_by_flips(_confdata.seed());
    return !regulars_reachable;
  }
  
  const bool ComputeTriangs::seed_heuristic_flipping_to_regular() {
    if (_confdata.has_points() && RegularityCheck(_confdata, _confdata.seed()).is_regular()) {
      return true;
    }
    SymmetricFlipGraphAlgorithms sfg_algs(0,
					  _confdata,
					  _options,
					  _global_triangcache,
					  _local_triangcache);

    // the following function uses a random permutation _confdata.no() of times
    // and explores in dfs style by flips that are
    // lex-GKZ-increasing w.r.t. this permutation:
    bool regulars_reachable = sfg_algs.find_regular_by_heuristic_flips(_confdata.seed()); 
    return regulars_reachable;
  }

  int ComputeTriangs::run() {

    // // write TOPCOM header with version information:
    // global::write_topcom_header(std::cerr);

    // write header for computations concerning triangulations:
    write_header(MessageStreams::verbose());

    // terminate if non-consistent options have been chosen:
    if (_options.input_chiro() && (CommandlineOptions::check_regular() || CommandlineOptions::check_nonregular())) {
      MessageStreams::forced() << "regularity check not possible if only chirotope is given - exiting." << std::endl;
      exit(1);
    }
    if (_options.input_chiro() && CommandlineOptions::use_volumes()) {
      MessageStreams::forced() << "volumes not exploitable if only chirotope is given - exiting." << std::endl;
      exit(1);
    }

    // read input from stdin or file (depending on commandline options):
    read_input(std::cin);

    // preprocess according to options and command line options:
    preprocess_data();
    
    // compute according to options:
    if (_options.compute_all()) {
      MessageStreams::verbose() << "exploring all triangulations by extension ..." << std::endl;
     if (_options.find_min()) {
	MessageStreams::verbose() << "... checking for a triangulation of minimal cardinality ..." << std::endl;
	const std::pair<size_type, SimplicialComplex> mintriang_pair = findmin_by_extension();
	MessageStreams::verbose() << "... done." << std::endl;
	if (mintriang_pair.second.empty()) {
	  MessageStreams::result() << "[-, -]" << std::endl;
	}
	else {
	  MessageStreams::result() << mintriang_pair << std::endl;
	}
      }
      else {
	MessageStreams::verbose() << "... enumerating all triangulations up to symmetry ..." << std::endl;
	const size_type N = enumerate_by_extension();
	MessageStreams::verbose() << N << " symmetry classes of triangulations in total." << std::endl;
	MessageStreams::verbose() << "... done." << std::endl;
	if (!_options.output_triangs()) {
	  MessageStreams::result() << N << std::endl;
	}
      }
    }
    else {
      MessageStreams::verbose() << "seed: " << _confdata.make_seed() << std::endl;
      MessageStreams::verbose() << "containing " << _confdata.seed().card()
				<< " simplices" << std::endl;
      MessageStreams::verbose() << "using the following " << _confdata.seed().support().card() << " vertices: " 
				<< _confdata.seed().support() << std::endl;
      MessageStreams::verbose() << "... done." << std::endl;
      MessageStreams::verbose() << "computing symmetries of seed ..." << std::endl;
      _confdata.make_seed_symmetryptrs();
      MessageStreams::verbose() << "... done." << std::endl;
      MessageStreams::verbose() << _confdata.seed_symmetryptrs().first.size() << " symmetries in total in seed." << std::endl;
      MessageStreams::verbose() << "symmetries of seed:" << '\n'
			      << _confdata.seed_symmetryptrs().first << std::endl;
      MessageStreams::verbose() << "... done." << std::endl;
      if (_options.find_flips()) {
	MessageStreams::verbose() << "count all flips of seed ..." << std::endl;
	const TriangNode tn(0, _confdata.no(), _confdata.rank(), _confdata.seed());
	Options find_flip_options(_options);
	find_flip_options.remove_options(Options::ONE_FLIP_ONLY);
	const TriangFlips tf(_confdata,
			     find_flip_options,
			     tn,
			     _confdata.seed_symmetryptrs());
	MessageStreams::verbose() << tf.flips().size() << " flips in total." << std::endl;
	MessageStreams::verbose() << "... done." << std::endl;
	if (_options.output_triangs()) {
	  MessageStreams::result() << tf << std::endl;
	}
	else {
	  MessageStreams::result() << tf.flips().size() << std::endl;
	}
      }
      else {
	MessageStreams::verbose() << "exploring the flip-graph component of seed by flipping ..." << std::endl;
	if (CommandlineOptions::check_nonregular_comp()) {
	  MessageStreams::verbose() << "... checking whether seed is a non-regular-component triangulation ..." << std::endl;
	  MessageStreams::result() << std::boolalpha << seed_is_nonregularcomp() << std::endl;
	}
	else if (CommandlineOptions::check_possibly_nonregular_comp()) {
	  MessageStreams::verbose() << "... checking whether seed is possibly a non-regular-component triangulation ..." << std::endl;
	  MessageStreams::result() << std::boolalpha << !seed_heuristic_flipping_to_regular() << std::endl;
	}
	else if (CommandlineOptions::check_findregular()) {
	  MessageStreams::verbose() << "... checking whether seed is a regular-component triangulation ..." << std::endl;
	  MessageStreams::result() << std::boolalpha << !seed_is_nonregularcomp() << std::endl;
	}
	else if (CommandlineOptions::check_randomfindregular()) {
	  MessageStreams::verbose() << "... checking whether seed can be randomly flipped to a regular triangulation ..." << std::endl;
	  MessageStreams::result() << std::boolalpha << seed_heuristic_flipping_to_regular() << std::endl;
	}
	else if (CommandlineOptions::check_nonsubregular() || CommandlineOptions::check_nonsupregular()) {
	  bool result;
	  if (CommandlineOptions::check_nonsubregular()) {	    
	    MessageStreams::verbose() << "... checking whether seed is a non-subregular triangulation ..." << std::endl;
	    result = seed_is_nonsubregular();
	    MessageStreams::result() << std::boolalpha << result << std::endl;
	  }
	  if (CommandlineOptions::check_nonsupregular()) {
	    MessageStreams::verbose() << "... checking whether seed is a non-supregular triangulation ..." << std::endl;
	    result = seed_is_nonsupregular();
	    MessageStreams::result() << std::boolalpha << result << std::endl;
	  }
	}
	else {
	  MessageStreams::verbose() << "... enumerating flip-graph component of seed up to symmetry ..." << std::endl;
	  const size_type N = enumerate_by_flips();
	  MessageStreams::verbose() << N << " symmetry classes of triangulations in total." << std::endl;
	  MessageStreams::verbose() << "... done." << std::endl;
	  if (!_options.output_triangs()) {
	    MessageStreams::result() << N << std::endl;
	  }
	}
      }
    }
    return Signal::exit_value();
  }

}; // namespace topcom

// eof ComputeTriangs.cc
