/* 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: stack.cc,v 1.12 2005/11/11 10:38:59 gawrilow Exp $"

#include <cstdlib>
#include <argv_options.h>
#include <labels.h>
#include <Poly.h>
#include <Rational.h>
#include <IncidenceMatrix.h>
#include <PowerSet.h>
#include <Graph.h>
#include <Matrix.h>
#include <Vector.h>
#include <bisector.h>

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

namespace polymake { namespace polytope {

namespace {

template <typename _Vector>
void nearest_vertex(const GenericVector<_Vector,Rational>& F, const Vector<Rational>& F_normal,
		    const Vector<Rational>& Fb, Rational& min_ratio)
{
   Rational r=F*F_normal;
   if (r>0) {
      r=F*Fb / r;
      if (r<min_ratio) min_ratio=r;
   }
}

Vector<Rational>
compute_new_vertex(const Matrix<Rational>& Facets, const Matrix<Rational>& Vertices,
		   const Vector<Rational>& Vb, const IncidenceMatrix<>& VIF,
		   const Graph<>& DG, const Set<int>& stack_facets, int sf, const Rational& lift_factor)
{
   const Vector<Rational> Fb=average(rows(Vertices.minor(VIF[sf],All)));
   Vector<Rational> F_normal=Facets[sf];
   F_normal[0] = -(F_normal.slice(1)*Fb.slice(1));	// facet normal thru facet barycenter
   Rational min_ratio=(Vb-Fb)*F_normal/sqr(F_normal);	// furthest position: mirror opposite of VERTEX_BARYCENTER

   for (Entire<Graph<>::neighbor_list>::const_iterator nb=entire(DG.neighbors(sf)); !nb.at_end();  ++nb) {
      if (stack_facets.contains(*nb))
	 nearest_vertex(bisector(Facets[sf], Facets[*nb], Vertices[ (VIF[sf] * VIF[*nb]).front() ]),
			F_normal, Fb, min_ratio);
      else
	 nearest_vertex(Facets[*nb], F_normal, Fb, min_ratio);
   }
   return Fb-min_ratio*lift_factor*F_normal;
}

} // end unnamed namespace

void stack(Poly& p_out, Poly& p_in, Set<int> stack_facets, const argv_option *options)
{
   const Rational lift_factor= options[lift] ? Rational(options[lift]) : Rational(1,2);

   if (options[lift] && (lift_factor<=0 || lift_factor>=1))
      throw std::runtime_error("lift factor must be between 0 and 1");

   const int dim=p_in.give("DIM");
   if (dim<=2)
      throw std::runtime_error("dimension too low to distinguish between simpliciality and cubicality");

   const bool simplicial=p_in.give("SIMPLICIAL"),
              cubical=p_in.give("CUBICAL");
   if (!simplicial && !cubical)
      throw std::runtime_error("polytope neither simplicial nor cubical");

   const IncidenceMatrix<> VIF=p_in.give("VERTICES_IN_FACETS");
   const int n_vertices=VIF.cols(), n_facets=VIF.rows();

   if (stack_facets.empty()) {
      stack_facets=sequence(0,n_facets);
   } else {
      if (stack_facets.front() < 0 || stack_facets.back() >= n_facets)
	 throw std::runtime_error("facet numbers out of range");
   }
   const int n_stack_facets=stack_facets.size();

   const int n_vertices_out= n_vertices + n_stack_facets * (simplicial ?     1 : 1 << dim-1),
	     n_facets_out  = n_facets   + n_stack_facets * (simplicial ? dim-1 : 2*(dim-1));
   p_out.take("DIM") << dim;
   p_out.take("N_VERTICES") << n_vertices_out;

   IncidenceMatrix<> VIF_out(n_facets_out, n_vertices_out);
   // copy untouched facets
   Rows< IncidenceMatrix<> >::iterator new_facet=rows(VIF_out).begin();
   new_facet=std::copy(entire(rows(VIF.minor(~stack_facets,All))), new_facet);

   std::vector<std::string> labels, facet_labels;
   if (options[relabel]) {
      labels.resize(n_vertices_out);  facet_labels.resize(n_facets);
      read_labels(p_in, "VERTEX_LABELS", labels.begin(), n_vertices);
      read_labels(p_in, "FACET_LABELS", facet_labels.begin(), n_facets);
   }

   Matrix<Rational> Vertices, Facets, Vertices_out;
   Vector<Rational> Vb;
   if (!options[no_coordinates]) {
      p_in.give("VERTICES") >> Vertices;
      p_in.give("FACETS") >> Facets;
      p_in.give("VERTEX_BARYCENTER") >> Vb;
      Vertices_out=Vertices / zero_matrix<Rational>(n_vertices_out-n_vertices, Vertices.cols());
   }

   if (simplicial) {
      int nv=n_vertices;	// new vertex
      for (Entire< Set<int> >::iterator sf=entire(stack_facets);  !sf.at_end();  ++sf, ++nv) {
	 // new facet = old facet - one of its vertices + new vertex
	 for (Subsets_less_1<const IncidenceMatrix<>::row_type&>::const_iterator
		 ridges=entire(all_subsets_less_1(VIF[*sf])); !ridges.at_end();  ++ridges, ++new_facet) {
	    *new_facet=*ridges;
	    new_facet->push_back(nv);
	 }
      }

      if (!options[no_coordinates]) {
	 const Graph<> DG=p_in.give("DUAL_GRAPH");
	 Rows< Matrix<Rational> >::iterator nv=rows(Vertices_out).begin()+n_vertices;
	 for (Entire< Set<int> >::iterator sf=entire(stack_facets);  !sf.at_end();  ++sf, ++nv) {
	    *nv=compute_new_vertex(Facets,Vertices,Vb,VIF,DG,stack_facets,*sf,lift_factor);
	 }
      }

      if (options[relabel]) {
	 std::vector<std::string>::iterator nl=labels.begin()+n_vertices;
	 for (Entire< Set<int> >::iterator sf=entire(stack_facets);  !sf.at_end();  ++sf, ++nl)
	    *nl = "f(" + facet_labels[*sf] + ')';
      }

   } else {
      // cubical
      const Graph<> DG=p_in.give("DUAL_GRAPH");

      int first_new_vertex=n_vertices;
      const int vertices_per_facet=1<<dim-1;
      std::vector<int> new_neighbors(n_vertices);

      for (Entire< Set<int> >::iterator sf=entire(stack_facets);  !sf.at_end();  ++sf) {
	 *new_facet=sequence(first_new_vertex, vertices_per_facet);
	 ++new_facet;
	 std::copy(entire(sequence(first_new_vertex, vertices_per_facet)), select(new_neighbors, VIF[*sf]).begin());

	 for (Entire<Graph<>::neighbor_list>::const_iterator nb=entire(DG.neighbors(*sf));
	      !nb.at_end();  ++nb, ++new_facet) {
	    const Set<int> ridge=VIF[*sf] * VIF[*nb];
	    *new_facet = ridge;
	    *new_facet += assure_ordered(select(new_neighbors,ridge));
	 }

	 if (!options[no_coordinates]) {
	    const Vector<Rational> Apex=compute_new_vertex(Facets,Vertices,Vb,VIF,DG,stack_facets,*sf,lift_factor);
	    for (Entire<IncidenceMatrix<>::row_type>::const_iterator nb=entire(VIF[*sf]); !nb.at_end();  ++nb) {
	       Vertices_out[new_neighbors[*nb]] = (Apex + Vertices[*nb])/2;
	    }
	 }

	 if (options[relabel]) {
	    for (Entire< IncidenceMatrix<>::row_type >::const_iterator v=entire(VIF[*sf]); !v.at_end();  ++v)
	       labels[new_neighbors[*v]] = "f(" + facet_labels[*sf] + ")-" + labels[*v];
	 }
	 first_new_vertex += vertices_per_facet;
      }
   }

   p_out.take("VERTICES_IN_FACETS") << VIF_out;
   p_out.take("SIMPLICIAL") << simplicial;
   p_out.take("CUBICAL") << cubical;

   if (!options[no_coordinates])
      p_out.take("VERTICES") << Vertices_out;
   if (options[relabel])
      p_out.take("VERTEX_LABELS") << labels;
}

} }

using namespace polymake;

/** @file stack
 *
 *  Stack a (simplicial or cubical) polytope over one or more of its facets.
 *
 *  For each specified facet, the barycenter of its vertices is lifted along the normal vector of the facet.
 *  In the simplicial case, this point is directly added to the vertex set, thus building a pyramid over
 *  the facet. In the cubical case, this pyramid is truncated by a hyperplane parallel to the original facet
 *  at its half height. This way, the property of being simplicial or cubical is preserved in both cases.
 *
 *  The keyword @c all means all facets of the original polytope.
 *
 *  Parameter $lf$ controls the exact coordinates of the new vertices.
 *  It should be a rational number between 0 and 1, which expresses the ratio of the distance between the
 *  new vertex and the stacked facet, to the maximal possible distance. When $lf=0$, the new vertex would lie 
 *  on the original facet. $lf=1$ corresponds to the opposite extremal case, where the new vertex
 *  hit the hyperplane of some neighbor facet. As an additional restriction, the new vertex can't
 *  lie further from the facet as the vertex barycenter of the whole polytope. Default value for $lf$ is $1/2$.
 *
 *  Alternatively, the option @c -noc (no coordinates) can be specified to produce a
 *  pure combinatorial description of the resulting polytope.
 *
 *  The option @c -relabel creates an additional section @see VERTEX_LABELS.
 *  New vertices get labels 'f(FACET_LABEL)' in the simplicial case,
 *  and 'f(FACET_LABEL)-NEIGHBOR_VERTEX_LABEL' in the cubical case.
 *
 *  @synopsis stack <output_file> <input_file> { <facet> [ <facet> ... ] | all } [ -lift <lf> | -noc ] [ -relabel ]
 *
 *  @reading  DIM, VERTICES_IN_FACETS, SIMPLICIAL, CUBICAL, DUAL_GRAPH, VERTEX_LABELS, FACET_LABELS
 *  @writing  DIM, N_VERTICES, VERTICES_IN_FACETS, SIMPLICIAL, CUBICAL, VERTEX_LABELS
 *
 *  @index clients Producing a new polyhedron from others
 */

int main(int argc, const char* argv[])
{
   argv_option options[]={ { "-noc" }, { "-relabel" }, { "-lift", true } };
   if (!extract_options(argc,argv,2,options) ||
       argc < 4 ||
       options[lift] && options[no_coordinates]) {
      cerr << "usage: " << argv[0] << " <out_file> <in_file> { <facet> [ <facet> ... ] | all } [ -lift <lf> | -noc ] [ -relabel ]" << endl;
      return 1;
   }
   try {
      Set<int> stack_facets;
      if (argc>4 || strcmp(argv[3],"all")) {
	 for (int i=3; i<argc; ++i)
	    stack_facets += atoi(argv[i]);
	 if (stack_facets.size() != argc-3)
	    throw std::runtime_error("repeating facet numbers in the list");
      }

      Poly p_in(argv[2], ios::in);
      Poly p_out(argv[1], ios::out | ios::trunc);
      polytope::stack(p_out, p_in, stack_facets, options);
   }
   catch (const std::exception& e) {
      cerr << e.what() << endl;
      return 1;
   }
   return 0;
}
