/* 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: barycentric_subdivision.cc 9991 2010-12-12 23:36:28Z gawrilow $
*/

#include "polymake/client.h"
#include "polymake/topaz/barycentric_subdivision.h"
#include "polymake/list"
#include <sstream>

namespace polymake { namespace topaz {

Array< Set<int> > bs(const graph::HasseDiagram& HD)
{
   const int dim=HD.dim()-1;
   int d=dim+1;
   for (int i=2; i<=dim; ++i) d*=i;     // each old facet is divided in (dim+1)! cells
   Array< Set<int> > F(HD.nodes_of_dim(-1).size() * d);
   Array< Set<int> >::iterator f=F.begin();
  
   typedef Graph<Directed>::out_edge_list::const_iterator out_edge;
   typedef std::list<out_edge> stack_type;
   stack_type stack;
  
   // start with the "empty set" node - just for convenience
   stack.push_back(HD.out_edges(HD.bottom_node()).begin());
  
   d=0; // dimension of the facet accumulated in stack so far
   do {
      // complete the facet
      while (d<dim) {
         const int n=stack.back().to_node();
         stack.push_back(HD.out_edges(n).begin());
         ++d;
      }
        
      // copy the facet
      for (Entire<stack_type>::const_iterator s=entire(stack);  !s.at_end();  ++s)
         *f += s->to_node() - 1;        // account for the top or bottom node which does not contribute to the new complex
        
      ++f;
        
      // depth-first search to the next facet
      do {
         if (!(++stack.back()).at_end()) break;
         stack.pop_back();
      } while (--d>=0);
   } while (d>=0);
      
   return F;
}
  
Array<std::string> bs_labels(const graph::HasseDiagram& HD)
{
   Array<std::string> L(HD.nodes()-2);
   graph::HasseDiagram::faces_map_type::const_iterator f=HD.faces().begin(); ++f;  // skip the top(bottom) node
   std::ostringstream label;
   for (Entire< Array<std::string> >::iterator l=entire(L); !l.at_end(); ++l, ++f) {
      wrap(label) << *f;
      *l=label.str();
      label.str("");
    
      // convert spaces to commas
      size_t blank=0;
      while ((blank=l->find(' ',blank)) != std::string::npos)
         (*l)[blank++]=',';
   }
  
   return L;
}
  
Matrix<Rational> bs_geom_real(const Matrix<Rational>& old_coord,
                              const graph::HasseDiagram& HD)
{
   const int amb_dim = old_coord.cols();
   const int n_nodes = HD.nodes()-2;
   Matrix<Rational> new_coord(n_nodes,amb_dim);
  
   graph::HasseDiagram::faces_map_type::const_iterator f=HD.faces().begin(); ++f;  // skip the top(bottom) node
   for (Entire< Rows< Matrix<Rational> > >::iterator r=entire(rows(new_coord));  !r.at_end();  ++r, ++f) {
      accumulate_in( entire(select(rows(old_coord), *f)), operations::add(), *r );
      *r /= f->size();
   }
  
   return new_coord;
}
  
perl::Object barycentric_subdivision(perl::Object p_in, perl::OptionSet options)
{
   const bool is_PC= !p_in.isa("topaz::SimplicialComplex");

   perl::Object p_out(p_in.type());
   p_out.set_description() << "Barycentric subdivision of " << p_in.name() << endl;
    

   graph::HasseDiagram HD;
   if (is_PC) {
      p_in.give("TRIANGULATION.HASSE_DIAGRAM")>>HD;
      p_out.take("TRIANGULATION.FACETS")<<bs(HD);
      if (options["relabel"]) p_out.take("LABELS") << bs_labels(HD);
   }
   else {
      p_in.give("HASSE_DIAGRAM")>>HD;
      p_out.take("FACETS") << bs(HD);
      if (options["relabel"]) p_out.take("VERTEX_LABELS") << bs_labels(HD);
   }
    

   if (is_PC) {
      Matrix<Rational> old_coord = p_in.give("POINTS");
      p_out.take("POINTS") << bs_geom_real(old_coord, HD);
   }
   else if (options["geom_real"]) {
      Matrix<Rational> old_coord = p_in.give("GEOMETRIC_REALIZATION");
      p_out.take("GEOMETRIC_REALIZATION") << bs_geom_real(old_coord, HD);
   }

   return p_out;
}

UserFunction4perl("# @category Producing a new simplicial complex from others"
                  "# Create a simplicial complex as a barycentric subdivision of a given complex."
                  "# Each vertex in the new complex corresponds to a face in the old complex."
                  "# @param SimplicialComplex complex"
                  "# @option Bool relabel generate vertex labels from the faces of the original complex."
                  "# @option Bool geom_real read [[GEOMETRIC_REALIZATION]] of the input complex,"
                  "#  compute the coordinates of the new vertices and store them as"
                  "#  [[GEOMETRIC_REALIZATION]] of the produced complex.",
                  &barycentric_subdivision,
                  "barycentric_subdivision($ { relabel => 0, geom_real => 0 })");
} }

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