/* 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: common_refinement.cc 10182 2011-05-04 17:46:48Z kulas $
*/

#include "polymake/client.h"
#include "polymake/Rational.h"
#include "polymake/ListMatrix.h"
#include "polymake/Matrix.h"
#include "polymake/hash_map"
#include "polymake/IncidenceMatrix.h"

namespace polymake { namespace fan {

template <typename Coord>
perl::Object common_refinement(perl::Object f1, perl::Object f2)
{
   const int d=f1.give("FAN_DIM");
   const Array<Set<int> > max_cones1=f1.give("MAXIMAL_CONES");
   const Matrix<Coord> rays1=f1.give("RAYS");
   const Array<Set<int> > max_cones2=f2.give("MAXIMAL_CONES");
   const Matrix<Coord> rays2=f2.give("RAYS");
  
   ListMatrix<Vector<Coord> > rays=rays1/rays2;
   hash_map<Vector<Coord>, int> ray_map;
   int index=0;
   for (typename Entire<Rows<ListMatrix<Vector<Coord> > > >::const_iterator i=entire(rows(rays)); !i.at_end(); ++i)
      ray_map[*i]=index++;
   std::list<Set<int> > new_max_cones;
   perl::ObjectType cone_type=perl::ObjectType::construct<Coord>("Cone");

   Array<perl::Object> all_cones1(max_cones1.size());
   for (int i=0; i<max_cones1.size(); ++i) {
      all_cones1[i].create_new(cone_type);
      const Matrix<Coord> p1_cone_vert=rays1.minor(max_cones1[i],All);
      all_cones1[i].take("RAYS")<<p1_cone_vert;
   }
   Array<perl::Object> all_cones2(max_cones2.size());
   for (int i=0; i<max_cones2.size(); ++i) {
      all_cones2[i].create_new(cone_type);
      const Matrix<Coord> p1_cone_vert=rays2.minor(max_cones2[i],All);
      all_cones2[i].take("RAYS")<<p1_cone_vert;
   }

   for (Entire<Array<perl::Object> >::iterator i1=entire(all_cones1); !i1.at_end(); ++i1) {
      for (Entire<Array<perl::Object> >::iterator i2=entire(all_cones2); !i2.at_end(); ++i2) {
         perl::Object inters=CallPolymakeFunction("intersection", *i1, *i2);
         const int inters_dim=inters.give("CONE_DIM");
         if (inters_dim==d) {
            const Matrix<Coord> inters_rays=inters.give("RAYS");
            Array<int> ray_indices(inters_rays.rows());
            int index=0;
            for (typename Entire<Rows<Matrix<Coord> > >::const_iterator i=entire(rows(inters_rays)); !i.at_end(); ++i,++index) {
         
               const Vector<Coord> ray=*i;
               const typename hash_map<Vector<Coord>,int>::iterator rep=ray_map.find(ray);
               if (rep!=ray_map.end()) {
                  ray_indices[index]=rep->second;
               } else {
                  ray_indices[index]=rays.rows();
                  rays/=ray;
               }
            }
            Set<int> new_cone;
            for (Entire<sequence>::const_iterator j=entire(sequence(0,index)); !j.at_end(); ++j)
               if (ray_indices[*j]>=0) new_cone.insert(ray_indices[*j]);
            new_max_cones.push_back(new_cone);
         }
      }
   }

   perl::Object f_out("PolyhedralFan");
   f_out.take("FAN_DIM")<<d;
   f_out.take("RAYS")<<rays;
   f_out.take("MAXIMAL_CONES")<<new_max_cones;
   return f_out;
}

UserFunctionTemplate4perl("# Computes the common refinement of two complete fans."
                          "# @param PolyhedralFan f1"
                          "# @param PolyhedralFan f2"
                          "# @return PolyhedralFan",
                          "common_refinement<Coord>(PolyhedralFan<Coord>,PolyhedralFan<Coord>)");
} }

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