/* 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: fundamental_group.cc,v 1.9 2006/01/13 21:32:35 gawrilow Exp $"

#include <Poly.h>
#include <complex_tools.h>
#include <Graph.h>
#include <Bitset.h>
#include <PowerSet.h>

#include <ext/hash_map>
#include <sstream>

namespace polymake { namespace topaz {

namespace {

   typedef std::pair<int, int> gen_label;
   typedef std::list< std::pair<int,int> > relation;
   typedef std::pair< int, std::list<relation> > fp_group;
   
}

void fundamental_group(Poly& p)
{
   const Array< Set<int> > C=p.give("FACETS");
   Graph<nothing,bool> G;
   p.give("GRAPH") >> neighborhood_matrix(G);
   const bool is_connected = p.give("CONNECTED");
   if (!is_connected)
      throw std::runtime_error("fundamental_group: Complex must be connected.");
   
   int n_edges = 0;
   const int n_verts = G.nodes();
   Bitset connected_verts(n_verts);
   
   std::list<int> queue;
   queue.push_back(0); // 0 is a node!
   connected_verts.insert(0);
   while (!queue.empty() && n_edges != n_verts-1 ) {
      const int node = queue.front();
      queue.pop_front();
      Set<int> neighbors = G.neighbors(node);
      neighbors -=  connected_verts;
      for (Entire< Set< int > >::const_iterator ni = entire(neighbors); !ni.at_end(); ++ni) {
	 G.create_edge(node,*ni,true);
	 ++n_edges;
	 connected_verts.insert(*ni);
	 queue.push_back(*ni);
      }
   }
   
   std_ext::hash_map<gen_label,int> generators(n_edges);  // estimate for #generators
   std::list<std::string> gen_labels;
   int c=0;
   for (Entire< Edges< Graph<nothing,bool> > >::const_iterator e_it=entire(edges(G)); 
	!e_it.at_end(); ++e_it) {
      if (!*e_it) {
	 std::ostringstream label;
	 if (e_it.from_node() < e_it.to_node()) {
	    const gen_label gl(e_it.from_node(),e_it.to_node());
	    generators[gl] = c;
	    label << "g" << e_it.from_node() << "_" << e_it.to_node();
	 } else {
	    const gen_label gl(e_it.to_node(),e_it.from_node());
	    generators[gl] = c;
	    label << "g" << e_it.to_node() << "_" << e_it.from_node();
	 }
	 gen_labels.push_back(label.str());
	 ++c;
      }
   }
   
   std::list<relation> relations;
   const PowerSet<int> skeleton_2 = k_skeleton(C,2);
   for (Entire< PowerSet<int> >::const_iterator c_it=entire(skeleton_2); !c_it.at_end(); ++c_it) {
      relation rel;
      for (Entire< Set<int> >::const_iterator s_it=entire(*c_it); !s_it.at_end();) {
	 const int from_node = *s_it;
	 ++s_it;
	 if (s_it.at_end()) {
	    if (!G.edge(c_it->front(),from_node)) {
	       rel.push_back(std::pair<int,int>(generators[gen_label(c_it->front(),from_node)],-1));
	    }
	 } else {
	    if (!G.edge(from_node,*s_it)) {
	       rel.push_back(std::pair<int,int>(generators[gen_label(from_node,*s_it)],1));
	    }
	 }
      }
      relations.push_back(rel);
   }
   
   p.take("FUNDAMENTAL_GROUP") << fp_group(gen_labels.size(),relations);
   p.take("FUNDAMENTAL_GROUP_GEN_LABELS") << as_array(gen_labels);
}
   
} }

using namespace polymake;

/** @file fundamental_group
 *
 *  Calculate a finite representation of the fundamental group,
 *  which may easily be treated with GAP.
 *
 *  @synopsis fundamental_group <file> 
 *
 *  @reading  FACETS, GRAPH
 *  @writing  FUNDAMENTAL_GROUP
 */

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::fundamental_group(p);
   }
   catch (const std::exception& e) {
      cerr << e.what() << endl;
      return 1;
   }
   return 0;
}


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