/* Copyright (c) 1997-2006
   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: flag_vector.cc,v 1.5 2006/01/27 15:32:44 gawrilow Exp $"

#include <cstdlib>
#include <ctime>
#include <Poly.h>
#include <Array.h>
#include <HasseDiagram.h>
#include <Fibonacci.h>

namespace polymake { namespace polytope {
namespace {

   typedef Graph<nothing, nothing, directed> PartialLattice;
      
   void extendLattice(const HasseDiagram<>& F, PartialLattice& G, int k, int i)
   {
      // cut layer k from bottom node
      for (Entire<sequence>::const_iterator kn = entire(F.nodes_of_dim(k)); !kn.at_end(); ++kn)
	 G.delete_edge (F.bottom_node(), *kn);

      // investigate layer i
      for (Entire<sequence>::const_iterator in = entire(F.nodes_of_dim(i)); !in.at_end(); ++in)
      {
	 // connect layer i with bottom node
	 G.edge(F.bottom_node(), *in);
	 // connect layer i with layer k
	 for (Entire<sequence>::const_iterator kn = entire(F.nodes_of_dim(k)); !kn.at_end(); ++kn)
	    if (incl(F.node(*in), F.node(*kn)) == -1)
	       G.edge(*in, *kn);
      }
   }

   void shrinkLattice(const HasseDiagram<>& F, PartialLattice& G, int k, int i)
   {
      // clean up layer i
      for (Entire<sequence>::const_iterator in = entire(F.nodes_of_dim(i)); !in.at_end(); ++in)
      {
	 // cut layer i from bottom node
	 G.delete_edge (F.bottom_node(), *in);
	 // cut layer i from layer k
	 for (Entire<PartialLattice::out_edge_list>::const_iterator eit = entire(G.out_edges(*in)); !eit.at_end(); ++eit)
	    G.delete_edge(*in, eit.to_node());
      }

      // reconnect layer k to bottom node
      for (Entire<sequence>::const_iterator kn = entire(F.nodes_of_dim(k)); !kn.at_end(); ++kn)
	 G.edge(F.bottom_node(), *kn);
   }

   int countIncidences(const PartialLattice& G, int startElement)
   {
      if (G.out_degree(startElement) == 0)
	 return 1;
      int n = 0;
      for (Entire<PartialLattice::out_edge_list>::const_iterator eit = entire(G.out_edges(startElement)); !eit.at_end(); ++eit)
	 n += countIncidences(G, eit.to_node());
      return n;
   }

   void calcEntry(const HasseDiagram<>& F, PartialLattice& G, int k, Array<int>& fl, int& curEntry)
   {
      for (int i = k-2; i >= 0; --i)
      {
	 extendLattice(F,G,k,i);
	 calcEntry(F,G,i,fl,curEntry);
	 shrinkLattice(F,G,k,i);
      }
      fl[curEntry] = countIncidences(G, F.bottom_node());
      --curEntry;
   }
} // end anonymous namespace

void flag_vector (Poly& p)
{
   HasseDiagram<> F = p.give ("HASSE_DIAGRAM");
   const int d = F.dim();

   // create the start lattice (of a single point polytope)
   PartialLattice G (F.nodes());
   G.edge (F.bottom_node(), F.top_node());

   // create the flag vector
   int initialEntry = fibonacci_number(d)-1;
   Array<int> fl(initialEntry+1);

   // calc
   calcEntry(F,G,d,fl,initialEntry);

   // save
   p.take ("FLAG_VECTOR") << fl;

}
} } // end namespace polymake::polytope


using namespace polymake;

/** @file flag_vector
 *  Calculate (the non-redundant part of) the flag vector of a polytope.
 *
 *  @synopsis flag_vector <file>
 *
 *  @reading  HASSE_DIAGRAM
 *  @writing  FLAG_VECTOR
 *
 *  @index clients Combinatorics
 *  @author Axel Werner
 */

int main(int argc, const char *argv[])
{
   if (argc!=2) {
      cerr << "usage: " << argv[0] << " <file>" << endl;
      return 1;
   }
   try
   {
      Poly p (argv[1], ios::in | ios::out);
      polytope::flag_vector (p);
   }
   catch (const std::exception& e)
   {
      cerr << e.what() << endl;
      return 1;
   }
   return 0;
}
