/* Copyright (c) 1997-2004
   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: clip_graph.cc,v 1.3 2006/01/05 16:37:24 joswig Exp $"

#include <Rational.h>
#include <Poly.h>
#include <Graph.h>
#include <Vector.h>
#include <Array.h>
#include <Matrix.h>
#include <ListMatrix.h>

namespace {
   using namespace polymake;

   Rational mu_intersect(const Vector<Rational>& u, const Vector<Rational>& v, const Vector<Rational>& i) {
      // compute the intersection of the line segment [u,v] with the hyperplane defined by i
      // condition: <u,i> < 0 && <v,i> >= 0
      // return value depends on whether or not u is finite
      if (u[0]!=0)
	 return -(v*i)/((u-v)*i);
      else
	 return -(v*i)/(u*i);
   }

   Vector<Rational> intersect(const Vector<Rational>& u, const Vector<Rational>& v, Rational mu) {
      // compute the intersection of the line segment [u,v] with the hyperplane defined by i [not provided]
      // condition: <u,i> < 0 && <v,i> >= 0
      if (u[0]!=0)
	 return (1-mu)*v+mu*u;
      else
	 return v+mu*u;
   }
}

namespace polymake { namespace polytope {

void clip_graph(Poly& p, char* clipped_graph_section, char* vertices_section, char* graph_section, char* bounding_box_section)
{
   const Matrix<Rational> V=p.give(vertices_section);
   const int n=V.rows();

   const Graph<> G=p.give(graph_section);
   Graph< Vector<Rational> > truncG(G);  // copy the graph information in order to change it later
   const Matrix<Rational> BB=p.give(bounding_box_section);

   // check which vertices satisfy all the inequalities of the bounding box; note that the rays must be sorted out, too
   Array<bool> valid(n,true);
   for (int i=0; i<n; ++i) {
      truncG.node(i)=V[i];
      for (Entire< Rows< Matrix<Rational> > >::const_iterator ineq_it=entire(rows(BB)); valid[i] && !ineq_it.at_end(); ++ineq_it)
	 valid[i]=(V(i,0)!=0) && (V[i]*(*ineq_it)>=0);
   }

   Set<int> nodes_to_delete;
   for (int i=0; i<n; ++i)
      if (!valid[i]) {
	for (Entire<Graph<>::out_edge_list>::const_iterator e=G.out_edges(i).begin(); !e.at_end(); ++e) {
	   const int j=e.to_node();
	   if (valid[j]) {
      	      Entire< Rows< Matrix<Rational> > >::const_iterator ineq_it=entire(rows(BB));
	      while (!ineq_it.at_end() && (*ineq_it)*V[i]>=0) ++ineq_it;
	      // there must be at least one invalid inequality for V[i]
	      Rational mu=mu_intersect(V[i],V[j],*ineq_it);
	      for (++ineq_it; !ineq_it.at_end(); ++ineq_it)
		 if ((*ineq_it)*V[i]<0) {
		    Rational mu2=mu_intersect(V[i],V[j],*ineq_it);
		    if (mu2<mu) mu=mu2;
		 }
	      const int new_node=truncG.add_node(intersect(V[i],V[j],mu));
	      truncG.edge(j,new_node);
	   }
	}
	nodes_to_delete+=i;
      }

   truncG.delete_nodes(nodes_to_delete);
   p.take(clipped_graph_section) << truncG;
}

} } 

using namespace polymake;

/** @file clip_graph
 *
 *  Clip a graph with respect to a given bounding box.  Used for the visualiztaion of Voronoi diagrams.
 *
 *  @synopsis clip_graph <file> <clipped_graph_section> <vertices_section> <graph_section> <bounding_box_section>
 *
 *  @reading <vertices_section> <graph_section> <bounding_box_section>
 *  @writing <clipped_graph_section> 
 */

int main(int argc, char *argv[])
{
   if (argc!=6) {
      cerr << "usage: " << argv[0] << " <file> <clipped_graph_section> <vertices_section> <graph_section> <bounding_box_section>" << endl;
      return 1;
   }
   try {
      Poly p(argv[1], ios::in | ios::out);
      polytope::clip_graph(p,argv[2],argv[3],argv[4],argv[5]);
   } catch (const std::exception& e) {
      cerr << e.what() << endl;
      return 1;
   }
   return 0;
}
