/* 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: dgraph.cc,v 1.29 2006/03/07 18:06:42 gawrilow Exp $"

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

namespace polymake { namespace polytope {

void dgraph(Poly& p, bool inverse)
{
   typedef Graph<nothing,nothing,directed> Dgraph;
   Dgraph DG=p.give("GRAPH");

   bool upper_bound=true, lower_bound=true;

   Vector<Rational> obj=p.give("LINEAR_OBJECTIVE | ABSTRACT_OBJECTIVE");
   const bool linear_objective= p.given() == "LINEAR_OBJECTIVE";
   if (inverse) inv_sign(obj);

   Set<int> rays;
   if (linear_objective) {
      const Matrix<Rational> V=p.give("VERTICES");
      obj=V*obj;
      p.give("FAR_FACE") >> rays;
   }

   const Rational* obj_value=obj.begin();
   for (Entire< Nodes<Dgraph> >::iterator n=entire(nodes(DG));  !n.at_end();  ++n, ++obj_value) {
      if (rays.contains(n.index())) {
	 const int ray_orientation=sign(*obj_value);
	 if (ray_orientation>0) upper_bound=false;
	 if (ray_orientation<0) lower_bound=false;
	 if (ray_orientation>=0) n.out_edges().clear();
	 if (ray_orientation<=0) n.in_edges().clear();
      } else {
	 // affine vertex
	 for (Dgraph::out_edge_list::iterator e=n.out_edges().find_nearest(n.index(), operations::gt()); !e.at_end(); ) {
	    if (rays.contains(e.to_node())) {
	       ++e;
	       continue;
	    }
	    const int idiff=sign(*obj_value - obj[e.to_node()]);
	    if (idiff<=0)
	       n.in_edges().erase(e.to_node());
	    if (idiff>=0)
	       n.out_edges().erase(e++);
	    else
	       ++e;
	 }
      }
   }

   Set<int> minface, maxface;
   for (Entire< Nodes<Dgraph> >::iterator n=entire(nodes(DG));  !n.at_end();  ++n) {
      if (!n.in_degree() && n.out_degree()) minface.push_back(n.index());
      if (!n.out_degree() && n.in_degree()) maxface.push_back(n.index());
   }
   Rational minval, maxval;
   if (lower_bound) minval=obj[minface.front()];
   if (upper_bound) maxval=obj[maxface.front()];

   if (inverse) {
      std::swap(inv_sign(minval), inv_sign(maxval));
      std::swap(minface,maxface);
      std::swap(upper_bound,lower_bound);
   }

   p.take("DIRECTED_GRAPH") << DG;
   p.take("MAXIMAL_FACE")  << maxface;
   p.take("MINIMAL_FACE")  << minface;

   if (upper_bound)
      p.take("MAXIMAL_VALUE") << maxval;
   else
      p.take("MAXIMAL_VALUE") << Poly::undefined();

   if (lower_bound)
      p.take("MINIMAL_VALUE") << minval;
   else
      p.take("MINIMAL_VALUE") << Poly::undefined();
}

} }

using namespace polymake;

/** @file dgraph
 *
 *  Direct the graph of a polytope according to a linear or abstract objective function.
 *  The maximal and minimal values, which are attained by the objective function, as well
 *  as the minimal and the maximal face are written into separate sections.
 *
 *  The optional "+" or "-" signals whether the graph is directed with increasing or
 *  decreasing edges, respectively.  By default, the edges are increasing.
 *
 *  @synopsis dgraph <file> [+/-]
 *
 *  @reading  GRAPH, LINEAR_OBJECTIVE | ABSTRACT_OBJECTIVE, VERTICES, FAR_FACE
 *  @writing  DIRECTED_GRAPH, MINIMAL_VALUE, MAXIMAL_VALUE, MINIMAL_FACE, MAXIMAL_FACE
 */

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