/* Copyright (c) 1997-2006
   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: minkowski_sum.cc,v 1.19 2006/02/21 14:32:52 gawrilow Exp $"

#include <Rational.h>
#include <Poly.h>
#include <linalg.h>

namespace polymake { namespace polytope {

namespace {

template <typename E, typename Matrix1, typename Matrix2>
Matrix<E>
minkowski_sum(const GenericMatrix<Matrix1,E>& A, const GenericMatrix<Matrix2,E>& B)
{
   Matrix<E> result(A.rows()*B.rows(), A.cols(),
		    product(rows(A), rows(B), operations::add()).begin());
   result.col(0).fill(1);
   return result;
}

}

void minkowski_sum(Poly& p_out, const Rational& lambda1, Poly& p_in1, const Rational& lambda2, Poly& p_in2)
{
   const Matrix<Rational> V1=p_in1.give("VERTICES | POINTS"),
                          V2=p_in2.give("VERTICES | POINTS");

   if (V1.cols() != V2.cols())
      throw std::runtime_error("dimension mismatch");

   const Set<int> rays1=far_points(V1),
	          rays2=far_points(V2);

   const Matrix<Rational> P= rays1.empty() && rays2.empty()
	                     ? minkowski_sum(lambda1*V1,lambda2*V2)
	                     : minkowski_sum(lambda1*V1.minor(~rays1,All), lambda2*V2.minor(~rays2,All)) /
                               (sign(lambda1)*V1.minor(rays1,All)) /
                               (sign(lambda2)*V2.minor(rays2,All)) ;

   p_out.take("POINTS") << P;
}

} }

using namespace polymake;

/** @file minkowski_sum
 *
 *  Produces the polytope lambda*P+mu*Q, where * and + are scalar multiplication
 *  and Minkowski addition, respectively.
 *
 *  @synopsis minkowski_sum <output_file> <scalar1> <infile1> <scalar2> <infile2>
 *
 *  @reading  VERTICES | POINTS
 *  @writing  POINTS
 *
 *  @index clients Producing a new polyhedron from others
 */

int main(int argc, char *argv[]) {
   if (argc != 6) {
      cerr << "usage: " << argv[0] << " <out_file> <scalar1> <infile1> <scalar2> <infile2>" << endl;
      return 1;
   }
   try {
      Poly PQ(argv[1], ios::out|ios::trunc);
      const Rational lambda= Rational(argv[2]);
      Poly P(argv[3], ios::in);
      const Rational mu= Rational(argv[4]);
      Poly Q(argv[5], ios::in);
      polytope::minkowski_sum(PQ, lambda, P, mu, Q);
   }
   catch (const std::exception& e) {
      cerr << e.what() << endl;
      return 1;
   }
   return 0;
}
