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

#include <cstdlib>
#include <argv_options.h>
#include <labels.h>
#include <Array.h>
#include <Graph.h>
#include <Matrix.h>
#include <Rational.h>
#include <Vector.h>
#include <Poly.h>
#include <cutting_plane.h>

namespace {
  enum { no_coordinates, relabel, cutoff };
}

namespace polymake { namespace polytope {

namespace {
bool adjust_cutting_plane(const Matrix<Rational>& V, const Graph<>& G,
			  int vertex_number, const Rational& cutoff_factor,
			  Vector<Rational>& cutting_plane)
{
   const Rational value_at_v= V[vertex_number] * cutting_plane;
   const Rational closest_value= accumulate(V.minor(G.neighbors(vertex_number),All) * cutting_plane, operations::min());
   if (closest_value<=value_at_v) return false;
   cutting_plane[0]=-(cutoff_factor*closest_value + (1-cutoff_factor)*value_at_v);
   return true;
}
  
} // end unnamed namespace

void vertex_figure(Poly& p_out, Poly& p_in, const int& vertex_number, const argv_option* options)
{
   const IncidenceMatrix<> VIF=p_in.give("VERTICES_IN_FACETS");
   const Graph<> G=p_in.give("GRAPH");
   const int n_vertices=VIF.cols();

   const Rational cutoff_factor= options[cutoff] ? Rational(options[cutoff]) : Rational(1,2);
   if (options[cutoff] && (cutoff_factor<=0 || cutoff_factor>=1)) {
      throw std::runtime_error("vertex_figure: cutoff factor must be within (0,1]");
   }

   if (vertex_number < 0 || vertex_number >= n_vertices) {
      throw std::runtime_error("vertex_figure: vertex number out of range");
   }
   const int n_vertices_out=G.degree(vertex_number);

   IncidenceMatrix<> VIF_out=VIF.minor(VIF.col(vertex_number), G.neighbors(vertex_number));
   p_out.take("VERTICES_IN_FACETS") << VIF_out;

   if (options[no_coordinates]) {
      if (p_in.exists("DIM")) {
	 const int dim=p_in.give("DIM");
	 p_out.take("DIM") << dim-1;
      }
   } else {
      const Matrix<Rational> V=p_in.give("VERTICES"),
	                     F=p_in.give("FACETS"),
	                     AH=p_in.give("AFFINE_HULL");
      Vector<Rational> cutting_plane=cutting_plane_via_barycenter(V,G,vertex_number);
      if (!adjust_cutting_plane(V,G,vertex_number,cutoff_factor,cutting_plane)) {
	 cutting_plane=cutting_plane_via_normals(V,F,VIF,vertex_number);
	 adjust_cutting_plane(V,G,vertex_number,cutoff_factor,cutting_plane);
      }

      p_out.take("FACETS") << F.minor(VIF.col(vertex_number),All);
      p_out.take("AFFINE_HULL") << AH / cutting_plane;
   }

   if (options[relabel]) {
      Array<std::string> labels(n_vertices);
      read_labels(p_in, "VERTEX_LABELS", labels.begin(), n_vertices);
      Array<std::string> labels_out(n_vertices_out, select(labels,G.neighbors(vertex_number)).begin());
      p_out.take("VERTEX_LABELS") << labels_out;
   }
}

} }

using namespace polymake;

/** @file vertex_figure
 *
 *  Construct the vertex figure of the vertex $n$ of a polyhedron
 *  The vertex figure is a facet of the dual polytope.
 *
 *  $n$ is the number of the chosen vertex
 *
 *  Parameter $cf$ controls the exact location of the cutting hyperplane.
 *  It should be a rational number from $(0,1)$.
 *  By $cf=0$, the hyperplane would go through the chosen vertex, thus degenerating the vertex figure
 *  to a single point. By $cf=1$, the hyperplane would touch the nearest neighbor vertex of a polyhedron.
 *  Default value for $cf$ is $1/2$.
 *
 *  Alternatively, the option @c -noc (no coordinates) can be specified to produce a
 *  pure combinatorial description.
 *
 *  The option @c -relabel creates an additional section @see VERTEX_LABELS.
 *  The vertices inherit the labels from the corresponding neighbor vertices of the original polytope.
 *
 *  @synopsis vertex_figure <output_file> <input_file> <n> [ -cutoff <cf> | -noc ] [ -relabel ]
 *
 *  @reading  VERTICES, FACETS, VERTICES_IN_FACETS, GRAPH
 *  @writing  VERTICES
 *
 *  @index clients Producing a new polyhedron from others
 */

int main(int argc, const char *argv[]) {
   argv_option options[]={ { "-noc" }, { "-relabel" }, { "-cutoff", true } };
   if (!extract_options(argc,argv,3,options)) {
      cerr << "usage: " << argv[0] << ": <out_file> <in_file> <vertex> [ -cutoff <cf> | -noc ] [ -relabel ]" << endl;
      return 1;
   }
   try {
     Poly p_in(argv[2], ios::in);
     Poly p_out(argv[1], ios::out | ios::trunc);
     polytope::vertex_figure(p_out, p_in, atoi(argv[3]), options);
   }
   catch (const std::exception& e) {
      cerr << e.what() << endl;
      return 1;
   }
   return 0;
}
