/* 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: wreath.cc,v 1.5 2006/02/23 16:04:28 thilosch Exp $"

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

namespace {
   enum { primal, dual, relabel };
}

namespace polymake { namespace polytope {

namespace {

struct wreath_label {
   typedef std::string first_argument_type;
   typedef std::string second_argument_type;
   typedef std::string result_type;
    
   result_type operator() (first_argument_type l1, second_argument_type l2) const {
      return l2 + '^' + l1;
   }
};
  
Matrix<Rational>
wreath_coord(const Matrix<Rational>& V1, const Matrix<Rational>& V2)
{
   const int n_vertices1(V1.rows()), n_vertices2(V2.rows()), dim1(V1.cols()-1), dim2(V2.cols()-1), d(n_vertices2*dim1+dim2+1);
   Matrix<Rational> V(n_vertices1*n_vertices2,d);
    
   for (int i=0; i<n_vertices2; ++i) {
      for (int k=0; k<n_vertices1; ++k) {
	const int ik(i*n_vertices1+k);
	V(ik,0)=Rational(1);
	const int id1(i*dim1); 
	int j(1);

	//v_{ik,j} = 0 for 0 <= j <= k*dim1
	for ( ; j<=id1; ++j) V(ik,j)=0;

	//v_{ik,j} = v1_{i, j-k*dim1} for k*dim1 < j <= (k+1)*dim1
	for ( ; j<=id1+dim1; ++j) V(ik,j)=V1(k,j-id1)/V1(k,0);

	// v_{ik,j} = 0 for (k+1)*dim1 < j <= n_vertices*dim1
	for ( ; j<=n_vertices2*dim1; ++j) V(ik,j)=0;
	
	//v_{ik,j} = v2_{k,j-n_vertices2*dim1} for n_vertices2*dim1 < j < d
	for ( ; j<d; ++j) V(ik,j)=V2(i,j-n_vertices2*dim1)/V2(i,0);
      }
   }
   return V;
}
  
} // end unnamed namespace

void wreath(Poly& p_out, Poly& p_in1, Poly& p_in2, const argv_option* options)
{
   bool centered = p_in1.give("CENTERED");
   if(!centered) {
      throw std::runtime_error("<in_file1> must be CENTERED.");
   }
   centered = p_in2.give("CENTERED");
   if(!centered) {
      throw std::runtime_error("<in_file2> must be CENTERED.");
   }
   
   std::ostringstream section;
   std::ostringstream labels_section;
   if(options[primal]) {
      section << "VERTICES";
      labels_section << "VERTEX";
   } else if(options[dual]) {
      section << "FACETS";
      labels_section << "FACET";
   }
   const Matrix<Rational> M1=p_in1.give(section.str().c_str()),
                          M2=p_in2.give(section.str().c_str());
   const int n_rows1(M1.rows()), n_rows2(M2.rows()),
             n_rows_out(n_rows1*n_rows2);

   const Matrix<Rational> M_out = wreath_coord(M1, M2);
   p_out.take(section.str().c_str()) << M_out;
   if (options[relabel]) {
      std::vector<std::string> labels1(n_rows1), labels2(n_rows2),
	                       labels_out(n_rows_out);
      labels_section << "_LABELS";
      read_labels(p_in1, labels_section.str().c_str(), labels1.begin(), n_rows1);
      read_labels(p_in2, labels_section.str().c_str(), labels2.begin(), n_rows2);
      std::copy(entire(product(labels2, labels1, wreath_label())), labels_out.begin());
      p_out.take(labels_section.str().c_str()) << labels_out;
   }
}

} }

using namespace polymake;

/** @file wreath
 *
 *  Construct a new polytope as the wreath product of two given ones.
 *  Unbounded polyhedra are not allowed.
 *
 *  If the option @c -noc (no coordinates) is set, only combinatorial information is handled.
 *
 *  The option @c -relabel creates an additional section @see VERTEX_LABELS.
 *  The label of a new vertex corresponding to $v_1 &oplus; v_2$ will
 *  have the form "$LABEL_1*LABEL_2$".
 *
 *  @synopsis wreath <output_file> <input_file_1> <input_file_2> {-primal, -dual} [ -relabel ]
 *
 *  @reading  VERTICES (-primal), FACETS (-dual)
 *  @writing  VERTICES (-primal), FACETS (-dual)
 *
 *  @index clients Producing a new polyhedron from others
 */

int main(int argc, const char *argv[]) {
   argv_option options[]={ { "-primal" }, { "-dual" }, { "-relabel" } };
   if (argc <= 4 || !extract_options(argc,argv,3,options) || argc!=4) {
      cerr << "usage: " << argv[0] << " <out_file> <in_file_1> <in_file_2>  {-primal, -dual} [ -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::wreath(p_out, p_in1, p_in2, options);
   }
   catch (const std::exception& e) {
      cerr << e.what() << endl;
      return 1;
   }
   return 0;
}
