/* 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 $$Id: t_simplicial_product.cc,v 1.5 2005/08/09 14:23:34 witte Exp $"

#include <Poly.h>
#include <Matrix.h>
#include <Vector.h>
#include <PowerSet.h>
#include <Array.h>
#include <argv_options.h>
#include <Rational.h>
#include <Map.h>

#include <list>
#include <string>


namespace polymake { namespace topaz {

namespace {

   enum { no_labels, geom_real, collor_cons};

   // m_set is an m-element set of {0,...,m+n-1}
   Array<bool> shuffle_from_m_set(const int m, const int n, const Set<int>& m_set) {
      Array<bool> shuffle(m+n-2,false);
      for (Entire< Set<int> >::const_iterator it=entire(m_set); !it.at_end(); ++it)
	 shuffle[*it]=true;
      return shuffle;
   }

   // m_set is an m-element set of {0,...,m+n-1}
   Set<int> facet_from_m_set(const std::list<int>& s1, const std::list<int>& s2,
			     const Matrix<int>& vert_map, const Set<int>& m_set) {
      //      cout << m_set << " -- " << shuffle_from_m_set(s1.size(),s2.size(),m_set) << endl;
      std::list<int>::const_iterator v1=s1.begin();
      std::list<int>::const_iterator v2=s2.begin();
      Set<int> facet;
      facet += vert_map(*v1,*v2);
      //      cout << *v1 << "," << *v2 << " -- " << vert_map(*v1,*v2) << endl;
      const Array<bool> shuffle = shuffle_from_m_set(s1.size(),s2.size(),m_set);
      for (Entire< Array<bool> >::const_iterator it=entire(shuffle); !it.at_end(); ++it) {
	 if (*it)  ++v1;
	 else      ++v2;
	 facet += vert_map(*v1,*v2);
	 //	 cout << *v1 << "," << *v2 << " -- " << vert_map(*v1,*v2) << endl;
      }
      
      //      cout << "facet: " << facet << endl;
      return facet;
   }
   

   Array<int> colllor_cons_ordering(const Array<int>& C) {
      Array<int> ordering(C.size());

      Set<int> colors;
      Map< int,std::list<int> > vert_of_color;
      for (int i=0; i<C.size(); ++i) {
	 colors += C[i];
	 vert_of_color[ C[i] ].push_back(i);
      }
    
      int i=0;
      for (Entire<  Set<int> >::const_iterator c=entire(colors); !c.at_end(); ++c)
	 for (Entire< std::list<int> >::const_iterator v=entire(vert_of_color[*c]);
	      !v.at_end(); ++v, ++i)
	    ordering[i] = *v;

      return ordering;
   }
   
}  // end empty namespace
   
void t_simplicial_product(Poly& p_out, Poly& p_in1, Poly& p_in2,
			const char* vert_order_section1, const char* vert_order_section2,
			const argv_option *options, const std::string& comment)
{
   const Array< Set<int> > C1 = p_in1.give("FACETS");
   const Array< Set<int> > C2 = p_in2.give("FACETS");
   const int n_vert1 = p_in1.give("N_VERTICES");
   const int n_vert2 = p_in2.give("N_VERTICES");

   // read orderings or set default
   Array<int> order1(n_vert1), order2(n_vert2);

   if (vert_order_section1 == "")
      if (options[collor_cons]) {
	 const bool is_balanced = p_in1.give("BALANCED");
	 if (!is_balanced)
	    throw std::runtime_error("t_simplicial_product: Complex 1 is not BALANCED");
	 const Array<int> coloring = p_in1.give("COLORING");
	 order1 = colllor_cons_ordering(coloring);
      } else
	 for (int i=0; i<n_vert1; ++i)
	    order1[i] = i;
   else
      order1 = p_in1.give(vert_order_section1);
   
   if (vert_order_section2 == "")
      if (options[collor_cons]) {
	 const bool is_balanced = p_in2.give("BALANCED");
	 if (!is_balanced)
	    throw std::runtime_error("t_simplicial_product: Complex 2 is not BALANCED");
	 const Array<int> coloring = p_in2.give("COLORING");
	 order2 = colllor_cons_ordering(coloring);
      } else
	 for (int i=0; i<n_vert2; ++i)
	    order2[i] = i;
   else
      order2 = p_in2.give(vert_order_section2);
   
   // compute vertex map
   Matrix<int> vert_map(n_vert1,n_vert2);
   int c=0;
   for (Entire< Array<int> >::const_iterator v2=entire(order2); !v2.at_end(); ++v2)
      for (Entire< Array<int> >::const_iterator v1=entire(order1); !v1.at_end(); ++v1, ++c)
	 vert_map(*v1,*v2) = c;
   
   //   cout << "order1\n" << order1 << "\norder2\n" << order2
   //	<< "\nvert_map\n" << vert_map << endl;

   std::list< Set<int> > F;
   for (Entire< Array< Set<int> > >::const_iterator f1=entire(C1); !f1.at_end(); ++f1)
      for (Entire< Array< Set<int> > >::const_iterator f2=entire(C2); !f2.at_end(); ++f2) {
	 //	 cout << "\n-------------  " << *f1 << " X " << *f2 << "  -------------\n";
	 const int m=f1->size()-1;
	 const int n=f2->size()-1;

	 std::list<int> s1,s2;
	 for (int i=0; i<n_vert1; ++i)
	    if (f1->contains(order1[i]))
	       s1.push_back(order1[i]);
	 for (int i=0; i<n_vert2; ++i)
	    if (f2->contains(order2[i]))
	       s2.push_back(order2[i]);
	 //	 cout << "ordered: " << s1 << " X " << s2 << endl;

	 // compute faces
	 Subsets_of_k<sequence> enumerator(range(0,m+n-1), m);
	 for (Subsets_of_k<sequence>::iterator m_set=entire(enumerator); !m_set.at_end(); ++m_set)
	    F.push_back(facet_from_m_set(s1,s2,vert_map,*m_set));
      }
   
   p_out.take("DESCRIPTION") << comment;
   p_out.take("FACETS") << as_array(F);

   if (!options[no_labels]) {
      const Array<std::string> L1 = p_in1.give("VERTEX_LABELS");
      const Array<std::string> L2 = p_in2.give("VERTEX_LABELS");
      Array<std::string> L(L1.size()*L2.size());

      c=0;
      for (Entire< Array<int> >::const_iterator v2=entire(order2); !v2.at_end(); ++v2)
	 for (Entire< Array<int> >::const_iterator v1=entire(order1); !v1.at_end(); ++v1, ++c) {
	    std::ostringstream label;
	    label << "(" << L1[*v1] << "," << L2[*v2] << ")";
	    L[c] = label.str();
	 }

      p_out.take("VERTEX_LABELS") << L;
   }

   if (options[geom_real]) {
      const Matrix<Rational> GR1 = p_in1.give("GEOMETRIC_REALIZATION");
      const Matrix<Rational> GR2 = p_in2.give("GEOMETRIC_REALIZATION");
      Matrix<Rational> GR(GR1.rows()*GR2.rows(),GR1.cols()+GR2.cols());
      c=0;
      for (Entire< Array<int> >::const_iterator v2=entire(order2); !v2.at_end(); ++v2)
	 for (Entire< Array<int> >::const_iterator v1=entire(order1); !v1.at_end(); ++v1, ++c)
	    GR[c] = GR1[*v1] | GR2[*v2];

      p_out.take("GEOMETRIC_REALIZATION") << GR;
   }
}
   
} }

using namespace polymake;

/** @file t_simplicial_product
 *
 *  Computes the simplicial product of two complexes.
 *
 *  Vertexorderings may be given by specifying sections.
 *
 *  @synopsis t_simplicial_product <out_file> <in_file_1> <in_file_2> [ <vertex_order_section1> [ <vertex_order_section1> ] ] [ -nol ] [ -geom_real ] [ -collor_cons ]
 *
 *  @reading  FACETS, N_VERTICES, may read <vertex_order_section1>, may read <vertex_order_section2>, may read GEOMETRIC_REALIZATION, may read COLORING, may read BALANCED
 *  @writing  DESCRIPTION, FACETS, may write VERTEX_LABELS, may write GEOMETRIC_REALIZATION
 *
 *  @index clients Producing a new simplicial complex from others
 */

int main(int argc, const char* argv[])
{
   argv_option options[] = { { "-nol" }, { "-geom_real" }, { "-collor_cons" }};
   if( !extract_options(argc,argv,2,options) || argc<4 || argc>6 ) {
      cerr << "usage: " << argv[0] << " <out_file> <in_file_1> <in_file_2> [ <vertex_order_section1> [ <vertex_order_section1> ] ] [ -nol ] [ -geom_real ] [ -collor_cons ]" << 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, p_in1.type());
      
      std::ostringstream comment;
      comment << "Simplicial product of" << argv[2] << " and " << argv[3] << ".";

      topaz::t_simplicial_product(p_out, p_in1, p_in2, (argc>4 ? argv[4] : ""), (argc>5 ? argv[5] : ""), options, comment.str());
   }
   catch (const std::exception& e) {
      cerr << e.what() << endl;
      return 1;
   }
   return 0;
}


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