/* 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: neighbors_cyclic.cc,v 1.2 2006/02/15 11:41:56 thilosch Exp $"

#include <list>
#include <Matrix.h>
#include <Graph.h>
#include <Array.h>
#include <IncidenceMatrix.h>
#include <Poly.h>
#include <Rational.h>
#include <linalg.h>
#include <argv_options.h>

namespace polymake { namespace surface {

namespace {

typedef Array< std::list<int> > CycleList;

template <typename IMatrix>
std::pair<CycleList,CycleList>
compute_cycles(const GenericIncidenceMatrix<IMatrix>& VIF, const Graph<>& DG) {

      const int n_facets=VIF.rows();
      CycleList VIF_cyclic(n_facets), DG_cyclic(n_facets);

      int nf=DG.neighbors(0).front();		// starting neighbor facet
      Set<int> nv=VIF[0] * VIF[nf];		// the ridge between 0 and nf
      int v2=(sequence(0, VIF.rows()) - VIF[0]).front();	// some vertex NOT on the facet 0

      VIF_cyclic[0].push_back(nv.front());  VIF_cyclic[0].push_back(nv.back());

      DG_cyclic[0].push_back(nf);
      std::list<int> Q;
      Q.push_back(0);

      while (!Q.empty()) {
	 int cf=Q.front(); Q.pop_front();
	 int v0=VIF_cyclic[cf].front(), v=VIF_cyclic[cf].back();
	 nf=DG_cyclic[cf].front();

	 while (true) {
	    nf=(VIF.col(v) * DG.neighbors(cf) - nf).front();
	    DG_cyclic[cf].push_back(nf);
	    v2=(VIF[nf] * VIF[cf] - v).front();
	    if (DG_cyclic[nf].empty()) {
	       // this ridge has the opposite direction in the neighbor facet border!
	       VIF_cyclic[nf].push_back(v2); VIF_cyclic[nf].push_back(v);
	       DG_cyclic[nf].push_back(cf);
	       Q.push_back(nf);
	    }
	    if (v2==v0) break;
	    VIF_cyclic[cf].push_back(v2);  v=v2;
	 }
      }
      return std::pair<CycleList,CycleList>(VIF_cyclic,DG_cyclic);
}

} // end unnamed namespace

void neighbors_cyclic_primal(Poly& p)
{
   const IncidenceMatrix<> VIF=p.give("FACETS");
   const Graph<> DG=p.give("DUAL_GRAPH");

   std::pair<CycleList,CycleList> cyclic= compute_cycles(VIF,DG);
   p.take("FACETS_CYCLIC") << cyclic.first;
   p.take("NEIGHBOR_FACETS_CYCLIC") << cyclic.second;
}

void neighbors_cyclic_dual(Poly& p)
{
   const IncidenceMatrix<> VIF=p.give("FACETS");
   const Graph<> G=p.give("GRAPH");

   std::pair<CycleList,CycleList> cyclic=compute_cycles(T(VIF),G);
   p.take("FTV_CYCLIC") << cyclic.first;
   p.take("ROTATION_SCHEME") << cyclic.second;
}

} }

using namespace polymake;

/** @file neighbors_cyclic
 *
 *  Convert the combinatorial description of a surface
 *  to the form suitable for visualization tools.
 *
 *  The rows of the vertex-facet incidence and the facet neighborhood matrices are rearranged
 *  in the facet boundary counterclockwise traversal order, if seen from the outside of the polytope.
 *
 *  In dual mode, the notions of facets and vertices are interchanged.
 *
 *  @synopsis neighbors_cyclic <file> { -primal | -dual }
 *
 *  @reading  in primal mode: FACETS, DUAL_GRAPH
 *            in dual mode:   FACETS, GRAPH
 *  @writing  in primal mode: FACETS_CYCLIC, NEIGHBOR_FACETS_CYCLIC
 *            in dual mode:   FTV_CYCLIC, ROTATION_SCHEME
 */

int main(int argc, const char *argv[])
{
   argv_option options[]={ { "-primal" }, { "-dual" } };
   enum { primal, dual };
   if (argc != 3 || !extract_options(argc,argv,1,options) || argc != 2) {
      cerr << "usage: " << argv[0] << " <file> { -primal | -dual }" << endl;
      return 1;
   }
   try {
      Poly p(argv[1], ios::in | ios::out);
      if (options[primal])
	 surface::neighbors_cyclic_primal(p);
      else
	 surface::neighbors_cyclic_dual(p);
   }
   catch (const std::exception& e) {
      cerr << e.what() << endl;
      return 1;
   }
   return 0;
}
