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

#include <cstdlib>
#include <argv_options.h>
#include <labels.h>
#include <vector>
#include <Rational.h>
#include <Poly.h>
#include <Matrix.h>
#include <IncidenceMatrix.h>

enum { no_coordinates, relabel };

namespace polymake { namespace polytope {

Matrix<Rational>
prism_coord(const Matrix<Rational>& V,
	    int& n_vertices, int& n_vertices_out,
	    const Set<int>& rays,
	    const Rational& z, const Rational& z_prime);

void prism(Poly& p_out, Poly& p_in, const Rational& z, const Rational& z_prime, const argv_option *options)
{
   int n_vertices=0, n_vertices_out=0;
   Set<int> rays;
   if (!options[no_coordinates])
      p_in.give("FAR_FACE") >> rays;

   if (options[no_coordinates] || p_in.exists("VERTICES_IN_FACETS")) {
      const IncidenceMatrix<> VIF=p_in.give("VERTICES_IN_FACETS");
      n_vertices=VIF.cols();
      n_vertices_out=2*n_vertices-rays.size();

      Set<int> far_facet=accumulate(cols(VIF.minor(All,rays)), operations::mul());
      if (far_facet.size() > 1)	// if size()>1 then FAR_FACE is not a facet
	 far_facet.clear();

      const IncidenceMatrix<> VIF_out=
	 rays.empty()
	 ? IncidenceMatrix<>( (VIF | VIF)				// side facet = original facet && its clone
			      / sequence(0,n_vertices)		// bottom facet = original polytope
			      / sequence(n_vertices,n_vertices) )	// top facet = cloned polytope
	 : IncidenceMatrix<>( (VIF.minor(~far_facet,All) | VIF.minor(~far_facet, ~rays))	// rays aren't cloned
			      / sequence(0,n_vertices)		//   and belong to both (top&bottom) facets
			      / (rays + sequence(n_vertices,n_vertices-rays.size())) );

      p_out.take("N_VERTICES") << n_vertices_out;
      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");
      const Matrix<Rational> V_out=prism_coord(V, n_vertices, n_vertices_out, rays, z, z_prime);
      p_out.take("VERTICES") << V_out;
   }

   if (options[relabel]) {
      std::vector<std::string> labels(n_vertices_out);
      read_labels(p_in, "VERTEX_LABELS", labels.begin(), n_vertices);
      const std::string tick="'";
      std::copy(entire(attach_operation(select(labels, sequence(0,n_vertices)-rays),
					constant(tick), operations::add())),
		labels.begin()+n_vertices);
      p_out.take("VERTEX_LABELS") << labels;
   }
}

} }

using namespace polymake;

/** @file prism
 *
 *  Make a prism over a polytope.
 *  The prism is the product of the input polytope and the interval [$z,z'$].
 *  The default interval is [$0,1$].  If $z$ != $0$, then $z'$ defaults
 *  to $-z$.
 *
 *  If the option @c -noc (no coordinates) is set, then only combinatorial information
 *  is handled. The input polytope is assumed to be bounded.
 *
 *  The option @c -relabel creates an additional section @see VERTEX_LABELS.
 *  The bottom facet vertices get the labels from the original polytope;
 *  the labels of their clones in the top facet get a tick (') appended.
 *
 *  @synopsis prism <output_file> <input_file> [ <z> [ <z'> ] | -noc ] [ -relabel ]
 *
 *  @reading  FAR_FACE, VERTICES or VERTICES_IN_FACETS, DIM or both
 *  @writing  VERTICES or VERTICES_IN_FACETS, N_VERTICES, DIM or both
 *
 *  @index clients Producing a new polyhedron from others
 */

int main(int argc, const char *argv[]) {
   argv_option options[]={ { "-noc" }, { "-relabel" } };
   if (!extract_options(argc,argv,2,options) ||
       argc > (options[no_coordinates] ? 3 : 5)) {
      cerr << "usage: " << argv[0] << " <out_file> <in_file> [ <z> [ <z'> ] | -noc ] [ -relabel ]" << endl;
      return 1;
   }
   try {
      const Rational z= argc>3 ? Rational(argv[3]) : Rational(0);
      const Rational z_prime= argc>4 ? Rational(argv[4]) : z ? -z : Rational(1);
      if (!options[no_coordinates] && z==z_prime) {
	 cerr << argv[0] << ": z and z' must be different" << endl;
	 return 1;
      }

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