/* 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.
*/

#include <Rational.h>
#include <Poly.h>
#include <Matrix.h>
#include <Vector.h>
#include <HasseDiagram.h>
#include <IncidenceMatrix.h>
#include <Graph.h>
#include <PowerSet.h>
#include <Array.h>
#include <bisector.h>

namespace polymake { namespace polytope {

void stellar_indep_faces(Poly& p_out, Poly& p_in, const char *faces_section)
{
   static const char* const req[]={ "VERTICES", "FACETS", "REL_INT_POINT", "VERTICES_IN_FACETS", "HASSE_DIAGRAM", "DUAL_GRAPH" };
   p_in.provide(req);
   Matrix<Rational> V=p_in.give("VERTICES");
   const Matrix<Rational> F=p_in.give("FACETS");
   const Vector<Rational> rel_int_point=p_in.give("REL_INT_POINT");
   const IncidenceMatrix<> VIF=p_in.give("VERTICES_IN_FACETS");
   const HasseDiagram<> HD=p_in.give("HASSE_DIAGRAM");
   typedef Graph<> graph;
   const graph DG=p_in.give("DUAL_GRAPH");
   const Array< Set<int> > in_faces=p_in.give(faces_section);

   PowerSet<int> spec_faces;
   for (Entire< Array< Set<int> > >::const_iterator it=entire(in_faces);
	!it.at_end(); ++it)
      spec_faces += *it;
   
   const int dim = HD.dim();
   
   int v_count = V.rows();
   int indep_vert = 0;
   V.resize (V.rows()+spec_faces.size(), V.cols());
   
   // iterate over dimensions dim-1..0 and over all faces of each dimension
   for (int d=dim-1; d>=0; --d) {
      for (Entire<sequence>::const_iterator it=entire(HD.nodes_of_dim(d));
	   !it.at_end(); ++it) {
	 const Set<int>& face = HD.node(*it);
	 if (spec_faces.contains(face)) {
	    
	    // produce all relevant inequalities = each neighbour (in the dual graph) of the nodes
	    // corresponding to the facets of star(face) produces one inequality
	    const Vector<Rational> m = average( rows(V.minor(face, All)) ) - rel_int_point;
	    Rational t_max = 2;
	    
	    // compute star(face)
	    Entire< Set<int> >::const_iterator s_it=entire(face);
	    Set<int> star_facets = VIF.col(*s_it);  ++s_it;
	    for ( ; !s_it.at_end(); ++s_it)
	       star_facets *= VIF.col(*s_it);
	    
	    for (Entire< Set<int> >::iterator st_it=entire(star_facets);
		 !st_it.at_end(); ++st_it) {
	       Set<int> neighbors = DG.neighbors(*st_it);
	       neighbors -= star_facets;
	       
	       for (Entire< Set<int> >::const_iterator n_it=entire(neighbors);
		    !n_it.at_end(); ++n_it) {
		  const int v= (VIF[*st_it] * VIF[*n_it]).front();
		  const Vector<Rational> inequ = bisector(F[*st_it], F[*n_it], V[v]);
		  
		  const Rational denominator = inequ * m;
		  if (denominator == 0)
		     continue;

		  const Rational t = -inequ * rel_int_point / denominator;
		  if (t < 0)
		     continue;
		  if (t<t_max)
		     t_max = t;
	       }
	    }

	    // add vertex
	    const Rational scale = (t_max + 1) / 2;
	    if (d==0) {  // replace subdevided vertex
	       V[face.front()] = rel_int_point + m * scale;
	       ++indep_vert;

	    } else {
	       V[v_count] = rel_int_point + m * scale;
	       ++v_count;
	    }
	 }
      }
   }

   if (V.rows() - v_count != indep_vert)
      throw std::runtime_error("stellar_indep_faces: non-faces specified");

   V.resize( v_count,V.cols() );
   p_out.take("VERTICES") << V;
}

} }

using namespace polymake;

/** @file stellar_indep_faces
 *
 *  Perform a stellar subdivision of the given faces.
 *
 *  The faces must have the following property:
 *  The open vertex stars of any pair of faces must be disjoint.
 *
 *  @synopsis stellar_indep_faces <out_file> <in_file> <faces_section>
 *
 *  @reading  VERTICES, FACETS, <faces_section>, REL_INT_POINT, VERTICES_IN_FACETS, HASSE_DIAGRAM, DUAL_GRAPH
 *  @writing  VERTICES
 *
 *  @index clients Producing a new polyhedron from others
 *
 *  @author Nikolaus Witte <witte@math.tu-berlin.de>
 */

int main(int argc, const char *argv[])
{
   if (argc!=4) {
      cerr << "usage: " << argv[0] << " <out_file> <in_file> <faces_section>" << endl;
      return 1;
   }
   try { 
      Poly p_in(argv[2], ios::in);
      Poly p_out(argv[1], ios::out | ios::trunc);
      polytope::stellar_indep_faces(p_out, p_in, argv[3]);
   }
   catch (const std::exception& e) {
      cerr << e.what() << endl;
      return 1;
   }
   return 0;
}


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