/* Copyright (c) 1997-2010
   Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Darmstadt, Germany)
   http://www.polymake.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.
--------------------------------------------------------------------------------
   $Project: polymake $$Id: mapping_polytope.cc 10061 2011-03-16 12:46:53Z herr $
*/

#include "polymake/client.h"
#include "polymake/vector"
#include "polymake/Rational.h"
#include "polymake/Matrix.h"
#include "polymake/Vector.h"
#include "polymake/Set.h"
#include "polymake/IncidenceMatrix.h"

namespace polymake { namespace polytope {
namespace {

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;
   }
};
}

perl::Object mapping_polytope(perl::Object p_in1, perl::Object p_in2, perl::OptionSet options)
{
   const int p=p_in1.CallPolymakeMethod("DIM"),
      q=p_in2.CallPolymakeMethod("DIM"),
      p2=p_in1.CallPolymakeMethod("AMBIENT_DIM"),
      q2=p_in2.CallPolymakeMethod("AMBIENT_DIM");
   if (p != p2 || q != q2)
      throw std::runtime_error("the two input polytopes must be full-dimensional");

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

   perl::Object p_out("Polytope<Rational>");
   p_out.set_description() << "Mapping polytope of " << p_in1.name() << " and " << p_in2.name() << endl;

   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;

   const bool relabel=options["relabel"];
   if (relabel) {
      std::vector<std::string> vertex_labels(n), facet_labels(m), labels_out(m*n);
      read_labels(p_in1, "VERTEX_LABELS", vertex_labels);
      read_labels(p_in2, "FACET_LABELS", facet_labels);

      copy(entire(pm::product(vertex_labels, facet_labels, product_label())), labels_out.begin());
      p_out.take("FACET_LABELS") << labels_out;
   }

   return p_out;
}

UserFunction4perl("# @category Producing a new polyhedron from others"
                  "# Construct a new polytope as the __mapping polytope__ of two polytopes //P1// and //P2//."
                  "# The mapping polytope is the set of all affine maps from R<sup>p</sup> to R<sup>q</sup>, that map //P1// into //P2//."
                  "# "
                  "# The label of a new facet corresponding to v<sub>1</sub> and h<sub>1</sub> will have the form"
                  "# \"v<sub>1</sub>*h<sub>1</sub>\"."
                  "# @param Polytope P1"
                  "# @param Polytope P2"
                  "# @option Bool relabel"
                  "# @return Polytope",
                  &mapping_polytope, "mapping_polytope(Polytope Polytope { relabel => undef })");
} }

// Local Variables:
// mode:C++
// c-basic-offset:3
// indent-tabs-mode:nil
// End:
