/* Copyright (c) 1997-2006
   Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Berlin, Germany)
   http://www.math.tu-berlin.de/polymake,  mailto:polymake@math.tu-berlin.de

   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation; either version 2, or (at your option) any
   later version: http://www.gnu.org/licenses/gpl.txt.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
*/

#ident "$Project: polymake $$Id: minimal_non_faces.cc,v 1.19 2006/03/09 16:21:35 gawrilow Exp $"

#include <PowerSet.h>
#include <Integer.h>
#include <Poly.h>
#include <HasseDiagram.h>
#include <Bitset.h>
#include <list>
#include <ext/hash_set>
#ifdef POLY_TIMING
#  include <stopwatch.h>
#endif

namespace polymake {
namespace topaz {
  
void minimal_non_faces(Poly& p)
{
   const HasseDiagram<> HD = p.give("HASSE_DIAGRAM");
   
#ifdef POLY_TIMING
   stopwatch timer;
   timer.start();
#endif
   
   const int dim = HD.dim()-1;  // dimension of the complex = HD.dim()-1
   std::list< Set<int> > min_non_faces;
   
   // determine the start level (the highest complete level)
   int start_dim = 1;
   while ( HD.nodes_of_dim(start_dim).size() == Integer::binom(dim,start_dim) )  ++start_dim;
   --start_dim;
   
   const int n_vertices=HD.nodes_of_dim(0).size(), top_node=HD.top_node();
   
   // iterate over all levels of HD and determine the minimal non-faces one above
   for (int d=start_dim; d<=dim; ++d) {
      
      // create hash set containing all faces of this dimension
      std_ext::hash_set< Set<int> > faces(HD.nodes_of_dim(d).size());
      for (Entire<sequence>::const_iterator it=entire(HD.nodes_of_dim(d)); !it.at_end(); ++it)
	 faces.insert(HD.node(*it));
      const std_ext::hash_set< Set<int> >::iterator faces_end = faces.end();  
      
      // iterate over all faces of this dimension
      for (Entire<sequence>::const_iterator it=entire(HD.nodes_of_dim(d)); !it.at_end(); ++it) {
	 const int n=*it;
	 const HasseDiagram<>::node_attr& f= HD.node(n);
	 
	 Bitset non_candidates(n_vertices);
	 for (Entire<HasseDiagram<>::out_edge_list>::const_iterator e=entire(HD.out_edges(n)); !e.at_end(); ++e)
	    if (e.to_node() != top_node)
	       non_candidates += (HD.node(e.to_node())-f).front();
#ifdef POLY_DEBUG
	 cout << "\nface " << n << ": " << f << "\nnon_candidates: " << non_candidates << endl;
#endif
	 // generate non-faces
	 for (Entire<HasseDiagram<>::in_edge_list>::const_iterator in_e=entire(HD.in_edges(n)); !in_e.at_end(); ++in_e) {
	    for (Entire<HasseDiagram<>::out_edge_list>::const_iterator out_e=entire(HD.out_edges(in_e.from_node())); !out_e.at_end(); ++out_e) {
	       const int nn = out_e.to_node();
	       if (n==nn)
		  continue;
	       
	       const int candidate = (HD.node(nn)-f).front();
	       if ( non_candidates.contains(candidate) || candidate<f.back() )
		  continue;
#ifdef POLY_DEBUG
	       cout << "candidate node " << nn << " (" << HD.node(nn) << ") with vertex: " << candidate << endl;
#endif
	       // test the candidate
	       bool is_minimal = true;
	       for (Entire< Subsets_less_1< Set<int> > >::const_iterator f_it=entire(all_subsets_less_1(f)); !f_it.at_end(); ++f_it)
		  if ( faces.find(*f_it + candidate) == faces_end ) {
		     is_minimal = false;
		     break;
		  }
	       if (is_minimal)  min_non_faces.push_back(f + candidate);
	       non_candidates += candidate;
#ifdef POLY_DEBUG
	       if (is_minimal)  cout << "ADDING min non-face: " << f + candidate << endl;
#endif
	    }
	 }
	 
      }  // end iterate over all faces of this dimension
   }
   
#ifdef POLY_TIMING
   timer.stop();
   cout << "facets:         " << HD.nodes_of_dim(-1).size() << endl;
   cout << "faces:          " << HD.nodes() << endl;
   cout << "min. non-faces: " << min_non_faces.size() << endl;
   cout << "time:           " << timer << " seconds\n" << endl;
#endif
   
   p.take("MINIMAL_NON_FACES") << as_array(min_non_faces);
}
   
} }

using namespace polymake;

/** @file minimal_non_faces
 *
 *  Find the inclusion-minimal non-faces of a complex.
 *
 *  @synopsis  minimal_non_faces <file>
 *
 *  @reading  HASSE_DIAGRAM
 *  @writing  MINIMAL_NON_FACES
 */

int main(int argc, const char* argv[])
{
   if (argc!=2) {
      cerr << "usage: " << argv[0] << " <file>" << endl;
      return 1;
   }
   try {
      Poly p(argv[1], ios::in | ios::out);
      topaz::minimal_non_faces(p);
   }
   catch (const std::exception& e) {
      cerr << e.what() << endl;
      return 1;
   }
   return 0;
}


// Local Variables:
// mode:C++
// c-basic-offset:3
// End:
