/* 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: vertex_colors.cc,v 1.23 2004/06/22 16:23:08 gawrilow Exp $"

#include <cstdlib>
#include <Rational.h>
#include <Poly.h>
#include <Matrix.h>
#include <Set.h>
#include <Graph.h>
#include <Vector.h>
#include <Array.h>
#include <color.h>

namespace polymake { namespace polytope {

void vertex_colors(Poly& p, const RGB<unsigned char>& min_rgb, const RGB<unsigned char>& max_rgb)
{
   Vector<double> obj=p.give("LINEAR_OBJECTIVE | ABSTRACT_OBJECTIVE");
   const bool linear_objective= p.given() == "LINEAR_OBJECTIVE";
   const Set<int> far_face=p.give("FAR_FACE"),
                  max_face=p.give("MAXIMAL_FACE"),
                  min_face=p.give("MINIMAL_FACE");

   if (linear_objective) {
      const Matrix<double> V=p.give("VERTICES");
      obj=V*obj;
   }

   const int n_vertices=obj.size();

   bool upper_bound=true, lower_bound=true;
   double max_val=0, min_val=0;

   if (p.defined("MAXIMAL_VALUE"))
      p.give("MAXIMAL_VALUE") >> max_val;
   else
      upper_bound=false;

   if (p.defined("MINIMAL_VALUE")) 
      p.give("MINIMAL_VALUE") >> min_val;
   else
      lower_bound=false;

   if (!upper_bound || !lower_bound) {
      const Graph<> G=p.give("GRAPH");
      const Set<int> neighbors_of_far=
	 accumulate(rows(neighborhood_matrix(G).minor(far_face,All)), operations::add()) - far_face;
      if (!upper_bound)
	 max_val=accumulate(obj.slice(neighbors_of_far), operations::max());
      if (!lower_bound)
	 min_val=accumulate(obj.slice(neighbors_of_far), operations::min());
   }

   double diff=max_val-min_val;
   if (!upper_bound) max_val+=0.1*diff;
   if (!lower_bound) min_val-=0.1*diff;
   diff=max_val-min_val;

   const RGB<unsigned char> white(255,255,255);
   Array< RGB<unsigned char> > vertex_colors(n_vertices, white);

   if (diff) {
      HSV maxcolor(max_rgb), mincolor(min_rgb);

      // If the maximal (minimal) face is affine, it is fully saturated,
      // otherwise the points towards the unbounded direction are made pale.
      if (!upper_bound) maxcolor[HSV::S]*=0.5;
      if (!lower_bound) mincolor[HSV::S]*=0.5;

      const double
	 Hmin=mincolor.hue(),
	 Hmax=maxcolor.hue(),
	 Hdiff=Hmax-Hmin,
	 Smin= mincolor.saturation(),
	 Smax= maxcolor.saturation(),
	 Sdiff=Smax-Smin,
	 Vmin=mincolor.value(),
	 Vmax=maxcolor.value(),
	 Vdiff=Vmax-Vmin;

      for (int v=0; v<n_vertices; ++v) {
	 if (far_face.contains(v)) {
	    if (max_face.contains(v))
	       vertex_colors[v]=maxcolor;
	    else if (min_face.contains(v))
	       vertex_colors[v]=mincolor;
	 } else {
	    const double k=(max_val-obj[v])/diff;
	    vertex_colors[v]=HSV(Hmax-k*Hdiff, Smax-k*Sdiff, Vmax-k*Vdiff);
	 }
      }
   }
   p.take("VERTEX_COLORS") << vertex_colors;
}

} }

using namespace polymake;

/** @file vertex_colors
 *
 *  Calculate RGB-color-values for each vertex depending on a linear or abstract objective function.
 *  Maximal and minimal affine vertices are colored as specified.  Far vertices (= rays) orthogonal
 *  to the linear function normal vector are white.  The colors for other affine vertices
 *  are linearly interpolated in the HSV color model.
 *
 *  If the objective function is linear and the corresponding LP problem is unbounded, then
 *  the affine vertices that would become optimal after the removal of the rays are painted
 *  pale.
 *
 *  @synopsis vertex_colors <file> <min_r> <min_g> <min_b> <max_r> <max_g> <max_b>
 *
 *  @reading  LINEAR_OBJECTIVE | ABSTRACT_OBJECTIVE, VERTICES, FAR_FACE,
 *            MAXIMAL_VALUE, MINIMAL_VALUE, MAXIMAL_FACE, MINIMAL_FACE, GRAPH
 *  @writing  VERTEX_COLORS
 */

int main(int argc, const char *argv[]) {
   if (argc != 8) {
      cerr << "usage: " << argv[0] << " <file> <min_r> <min_g> <min_b> <max_r> <max_g> <max_b>" << endl;
      return 1;
   }
   try {
     RGB<unsigned char> min_rgb(atoi(argv[2]), atoi(argv[3]), atoi(argv[4])),
			max_rgb(atoi(argv[5]), atoi(argv[6]), atoi(argv[7]));
     
     Poly p(argv[1], ios::in | ios::out);
     
     polytope::vertex_colors(p, min_rgb, max_rgb);
   }
   catch (const std::exception& e) {
      cerr << e.what() << endl;
      return 1;
   }
   return 0;
}
