/* 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: 2-face-sizes-simple.cc,v 1.4 2006/01/17 19:34:37 gawrilow Exp $"

#include <ext/hash_map>

#include <Integer.h>
#include <Poly.h>
#include <argv_options.h>
#include <Graph.h>
#include <IncidenceMatrix.h>

using namespace polymake;

typedef Graph<> graph;

namespace {

enum { primal, dual };

template <typename IMatrix>
void two_faces(const graph& G, const GenericIncidenceMatrix<IMatrix>& I, std_ext::hash_map<int,int>& two_face_map) {
#if __GNUC__==3 && __GNUC_MINOR__<=1
   (void)I.col(0);
#endif

   // Each adjacent pair of edges spans a 2-face.  In order to avoid double counting we enumerate adjacent pairs of
   // edges such that the middle vertex is maximal among all vertices of that 2-face.
   // The to_nodes in the out_edge_list come in ascending order.
   for (int n1=G.nodes()-1; n1>=0; --n1)
      for (Entire<graph::out_edge_list>::const_iterator n2(G.out_edges(n1).begin()); !n2.at_end() && n2.to_node()<n1; ++n2) {
	 for (Entire<graph::out_edge_list>::const_iterator n3(G.out_edges(n1).begin()); n3.to_node()<n2.to_node(); ++n3) {
	    const Set<int> facets_thru_all3(I.col(n1)*I.col(n2.to_node())*I.col(n3.to_node()));
	    
	    // compute the 2-face spanned by n1, n2.to_node() and n3.to_node()
	    Entire< Set<int> >::const_iterator it(entire(facets_thru_all3));
	    Set<int> this_face(I.row(*it));
	    for (++it ; !it.at_end(); ++it)
	       this_face*=I.row(*it);
	    
	    if (this_face.back()==n1) { // n1 largest vertex in 2-face; this avoids double counting
	       const int this_size(this_face.size());
	       ++two_face_map[this_size];
	    }
	 }
      }
} }


namespace polytope {

void two_face_sizes_simple(Poly& p, const argv_option options[]) {
   const graph G = options[primal]? p.give("GRAPH") : p.give("DUAL_GRAPH");
   const IncidenceMatrix<> I=p.give("VERTICES_IN_FACETS");
   std_ext::hash_map<int,int> two_face_map;
   
   if (options[primal]) {
      two_faces(G,I,two_face_map);
      p.take("TWO_FACE_SIZES") << two_face_map;
   } else {
      two_faces(G,T(I),two_face_map);
      p.take("SUBRIDGE_SIZES") << two_face_map;
   }
} }

/** @file 2-face-sizes-simple
 *
 *  Compute the sizes of all 2-faces of a simple polytope.
 *  Since this algorithm does not require the complete face lattice, it is much faster
 *  than the standard client @see 2-face-sizes.
 *
 *  @synopsis 2-face-sizes-simple <file> { -primal | -dual }
 *
 *  @reading  in primal mode: GRAPH, VERTICES_IN_FACETS
 *            in dual mode:   DUAL_GRAPH, VERTICES_IN_FACETS
 *  @writing  in primal mode: TWO_FACE_SIZES
 *            in dual mode:   SUBRIDGE_SIZES
 */
int main(int argc, const char *argv[]) {
   argv_option options[]={ { "-primal" }, { "-dual" } };
   if (argc != 3 ||
       !extract_options(argc,argv,1,options) ||
       argc != 2) {
      cerr << "usage: " << argv[0] << " <file> { -primal | -dual }" << endl;
      return 1;
   }
   try {
      Poly p(argv[1], ios::in | ios::out);
      polytope::two_face_sizes_simple(p,options);
   }
   catch (const std::exception& e) {
      cerr << e.what() << endl;
      return 1;
   }
   return 0;
}
