/* Copyright (c) 1997-2009
   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: is_vertex_decomposition.cc 9298 2009-09-02 23:10:56Z gawrilow $
*/

#include <polymake.h>
#include <complex_tools.h>

namespace polymake { namespace topaz {

bool is_vertex_decomposition(perl::Object p, const Array<int>& ShedVert, perl::OptionSet options)
{
   if (ShedVert.empty())
      throw std::runtime_error("no shedding vertices specified");

   const bool verbose=options["verbose"];

   const bool pure=p.give("PURE");
   if (!pure) {
      if (verbose) cout << "The complex is not pure." << endl;
      return false;
   }
   const int d=p.give("DIM");

   if (d==0) {
      const int n_vertices=p.give("N_VERTICES");
      if (ShedVert.size() > n_vertices) {
	 if (verbose) cout << "Too many shedding vertices specified." << endl;
	 return false;
      }
      Set<int> V(sequence(0,n_vertices));
      accumulate_in(entire(ShedVert), operations::sub(), V);
      if (!V.empty()) {
	 if (verbose) cout << "The complex is not completely decomposed." << endl;
	 return false;
      }
      return true;
   }
   if (d>3)
      throw std::runtime_error("is_vertex_decomposition: Dimension of the complex must be smaller than 4.");

   const bool is_MF=p.give("MANIFOLD");
   if (!is_MF) {
      if (verbose) cout << "Complex is not a manifold." << endl;
      return false;
   }

   HasseDiagram HD=p.give("HASSE_DIAGRAM");

   // for all v in ShedVert
   for (Entire< Array<int> >::const_iterator v_it=entire(ShedVert); !v_it.at_end(); ++v_it) {
      const int v=*v_it;

      // if the remaining complex consists of v only -> complex is decomposed
      const HasseDiagram::nodes_of_dim_set rest_vertex_nodes=HD.nodes_of_dim(0);
      if (rest_vertex_nodes.size()==1) {
	 if (HD.face(rest_vertex_nodes.front()).front() != v || !(++v_it).at_end()) {
	    if (verbose) cout << "Too many shedding vertices specified." << endl;
	    return false;
	 }
	 return true;
      }

      // compute the vertices of link(v) and remove star(v) from HD
      const Set<int> V_of_link = vertices_of_vertex_link(HD,v);
      remove_vertex_star(HD,v);

      // check the invariant:
      // dim == 0 -> true
      // dim > 0  -> HD is a manifold.
      const int dim = HD.dim()-1;
      if (dim==0) continue;

      // it suffices to check the link for all vertices of link(v)
      for (Set<int>::const_iterator l_it=V_of_link.begin(); !l_it.at_end(); ++l_it) {
	 const int w=*l_it;
	 const std::list< Set<int> > link=as_iterator_range(vertex_link_in_HD(HD,w));

	 if (dim==1 && !link.empty() && link.size()<3) continue;
	 if (dim==2 && !link.empty() && is_ball_or_sphere(link,int2type<1>())) continue;
	 if (dim==3 && !link.empty() && is_ball_or_sphere(link,int2type<2>())) continue;

	 if (verbose) cout << "The remaining complex after removing vertex star(" << v << ") is not a manifold." << endl;
	 return false;
      }
   }  // end for all v in ShedVert

   if (verbose) cout << "The complex is not completely decomposed." << endl;
   return false;
}

   UserFunction4perl("# Check whether a given ordered subset of the vertex set is a vertex decomposition.\n"
		     "# Works for 1-, 2- and 3-manifolds only!\n",
		     &is_vertex_decomposition, "is_vertex_decomposition(SimplicialComplex $ { verbose=>0 })");
} }
