/* Copyright (c) 1997-2005
   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: mapping_polytope.cc,v 1.4 2005/09/26 14:48:13 gawrilow Exp $"

#include <ctime>
#include <argv_options.h>
#include <labels.h>
#include <vector>
#include <Rational.h>
#include <Poly.h>
#include <Matrix.h>
#include <Vector.h>
#include <Set.h>
#include <IncidenceMatrix.h>

namespace polymake { namespace polytope {

namespace {

enum { relabel };

struct product_label {
   typedef std::string first_argument_type;
   typedef std::string second_argument_type;
   typedef std::string result_type;

   result_type operator() (const first_argument_type& v, const second_argument_type& f) const
   {
      return 'v' + v + '*' + 'F' + f;
   }
};

} // end unnamed namespace

void mapping_polytope(Poly& p_out, Poly& p_in1, Poly& p_in2, const argv_option *options)
{
   int p = p_in1.give("DIM"), q = p_in2.give("DIM");
   int p2 = p_in1.give("AMBIENT_DIM"), q2 = p_in2.give("AMBIENT_DIM");
   if (p != p2 || q != q2) {
      throw std::runtime_error("the two input polytopes must be full-dimensional");
   }

   Matrix<Rational> V = p_in1.give("VERTICES");
   Matrix<Rational> H = p_in2.give("FACETS");
   int n = V.rows(), m = H.rows();

   Matrix<Rational> H_out(m*n,q*(p+1)+1);

   int i = 0, j = 0, l = 0;
   for (Entire< Rows< Matrix< Rational > > >::const_iterator vi = entire(rows(V)); !vi.at_end(); ++vi, ++i) {
      j = 0;
      for (Entire< Rows< Matrix< Rational > > >::const_iterator fi = entire(rows(H)); !fi.at_end(); ++fi, ++j) {
	 H_out[l][0] = (*fi)[0];
	 for (int k = 0; k < q; ++k) {
	    H_out[l].slice(sequence(k*(p+1)+1,p+1)) = (*fi)[k+1]*(*vi);
	 }
	 ++l;
      }
   }
   p_out.take("FACETS") << H_out;

   if (options[relabel]) {
      std::vector<std::string> vertex_labels(n), facet_labels(m), labels_out(m*n);
      read_labels(p_in1, "VERTEX_LABELS", vertex_labels.begin(), n);
      read_labels(p_in2, "FACET_LABELS", facet_labels.begin(), m);
  
      std::copy(entire(pm::product(vertex_labels, facet_labels, product_label())), labels_out.begin());
      p_out.take("FACET_LABELS") << labels_out;
   }
}

} }

using namespace polymake;

/** @file mapping_polytope
 *
 *  Construct a new polytope as the mapping polytope of two
 *  polytopes P and Q. The mapping polytope is the set of
 *  all affine maps from R^p to R^q, that map P into Q.
 *
 *  The label of a new facet corresponding to $v_1$ and $h_1$ will
 *  have the form "v_1*h_1$".
 *
 *  @synopsis mapping_polytope <output_file> <input_file_1> <input_file_2> [ -relabel ]
 *
 *  @reading  DIM, AMBIENT_DIM of both input files VERTICES of input_file_1 and 
 *            FACETS of input_file_2
 *  @writing  FACETS
 *
 *  @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!=4) {
      cerr << "usage: " << argv[0] << " <out_file> <in_file_1> <in_file_2> [ -relabel ]" << endl;
      return 1;
   }
   try {
      Poly p_in1(argv[2], ios::in);
      Poly p_in2(argv[3], ios::in);
      Poly p_out(argv[1], ios::out | ios::trunc);
      polytope::mapping_polytope(p_out, p_in1, p_in2, options);
   }
   catch (const std::exception& e) {
      cerr << e.what() << endl;
      return 1;
   }
   return 0;
}
