/* 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 $$Version: 1.3.1 $$Id: cayley_embedding.cc,v 1.9 2004/09/20 16:35:37 gawrilow Exp $"

#include <argv_options.h>
#include <labels.h>
#include <vector>
#include <Rational.h>
#include <Poly.h>
#include <linalg.h>

enum { relabel };

namespace polymake { namespace polytope {

void cayley_embedding(Poly& p_out, Poly& p_in1, Poly& p_in2,
		      const Rational& z, const Rational& z_prime, const argv_option *options)
{
   const Matrix<Rational> V1=p_in1.give("VERTICES | POINTS"),
	                  V2=p_in2.give("VERTICES | POINTS");
   bool VERTICES_out= p_in1.given()=="VERTICES" && p_in2.given()=="VERTICES";

   const int n_vertices1=V1.rows(), n_vertices2=V2.rows(), d=V1.cols();
   if (V2.cols() != d)
      throw std::runtime_error("dimension mismatch");

   const Set<int> rays1=far_points(V1),
	          rays2=far_points(V2);
   if (VERTICES_out && !rays1.empty() && !rays2.empty()) VERTICES_out=false;

   const Matrix<Rational> V_out=
	 rays1.empty() && rays2.empty()
	 ? Matrix<Rational>( (V1 | same_element_vector(z, n_vertices1)) /
			     (V2 | same_element_vector(z_prime, n_vertices2)) )
	 : Matrix<Rational>( (V1 | same_element_sparse_vector(~rays1, z, n_vertices1)) /
			     (V2 | same_element_sparse_vector(~rays2, z_prime, n_vertices2)) );
   p_out.take(VERTICES_out ? "VERTICES" : "POINTS") << V_out;

   if (options[relabel]) {
      std::vector<std::string> labels(n_vertices1+n_vertices2);
      read_labels(p_in1, "VERTEX_LABELS", labels.begin(), n_vertices1);
      std::vector<std::string>::iterator l=labels.begin()+n_vertices1;
      read_labels(p_in2, "VERTEX_LABELS", l, n_vertices2);
      const char tick='\'';
      for (std::vector<std::string>::iterator l_end=labels.end(); l!=l_end; ++l)
	 (*l) += tick;
      p_out.take("VERTEX_LABELS") << labels;
   }
}

} }

using namespace polymake;

/** @file cayley_embedding
 *
 *  Create a Cayley embedding of two polytopes.
 *  The vertices of the first polytope get an extra coordinate $z$,
 *  and the vertices of the second one get $z'$.
 *
 *  Default values are $z=1$ and $z'=-z$.
 *
 *  The option @c -relabel creates an additional section @see VERTEX_LABELS.
 *  The vertices of the first polytope inherit the original labels unchanged;
 *  the vertex labels from the second polytope get a tick (') appended.
 *
 *  @synopsis cayley_embedding <output_file> <input_file1> <input_file2> [ <z> [ <z'> ] ] [ -relabel ]
 *
 *  @reading  VERTICES | POINTS
 *  @writing  VERTICES | POINTS
 *
 *  @index clients Producing a new polyhedron from others
 */

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

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