/* 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: toric_g_vector.cc,v 1.4 2006/03/09 16:21:35 gawrilow Exp $"

#include <cstdlib>
#include <ctime>
#include <Integer.h>
#include <Poly.h>
#include <Array.h>
#include <Vector.h>
#include <Matrix.h>
#include <list>
#include <Fibonacci.h>

using std::list;

namespace polymake { namespace polytope {
namespace {

   inline int calc_p(int n, int j)
   {
      return static_cast<int>(Integer::binom(n,j) - Integer::binom(n,j-1));
   }
      
   int calc_b(int deg, int v, int n, const Array<int>& Fib)
   {
      list<int> l;
      int curl = 0;
      int rd = deg;
      int rv = v;
      while (rd > 1)
	 if (rv >= Fib[rd-1])	// we have a 'd'
	 {
	    if ((curl % 2) != 0) // the previous exponent of the previous 'c' was not even...
	       return 0;	// ...so b(v,n) is 0
	    rv -= Fib[rd-1];
	    rd -= 2;
	    l.push_back (curl/2);
	    curl = 0;
	 }
	 else		// it's a 'c'
	 {
	    rd -= 1;
	    ++curl;
	 }
      if (rd == 1)
      {
	 ++curl;
      }
      if ((curl % 2) != 0)
	 return 0;
      curl /= 2;

      int r = l.size();
      int i = deg/2;
      int res = calc_p (n-2*i+2*curl, curl);
      while (!l.empty())
      {
	 curl = l.front();
	 l.pop_front();
	 res *= calc_p (2*curl, curl);
      }

      return ((i-r)%2==0 ? res : -res);
   }

   bool ends_with_c(int deg, int v, const Array<int>& Fib) // ...is there a faster way to tell?
   {
      bool endc = true;
      int rd = deg;
      int rv = v;
      while (rd > 1)
	 if (rv >= Fib[rd-1])	// we have a 'd'
	 {
	    rv -= Fib[rd-1];
	    rd -= 2;
	    endc = false;
	 }
	 else		// it's a 'c'
	 {
	    rd -= 1;
	    endc = true;
	 }
      if (rd == 1)		// there's a lonely 'c' and the end
	 endc = true;

      return endc;
   }
} // end anonymous namespace

void toric_g_vector(Poly & p)
{
   int d = p.give ("DIM");
   Vector<int> cd = p.give ("CD_INDEX_COEFFICIENTS");

   // precalculate the fibonacci-numbers
   const Array<int> Fib(d+1, fibonacci_numbers());

   // holds the g-vector
   Vector<int> gvec(d/2+1);
   gvec[0] = 1;

   for (int i = 1; i <= d/2; ++i)
   {			// calculate g_i in this loop
      int deg = 2*i;
      int max_cdmon = Fib[deg];
      int bigv = 0;	// count the full monomial seperately
      int degfill = d-deg;
      for (int v = 0; v < max_cdmon; ++v)
      {
	 int moncoeff = calc_b(deg,v,d,Fib);
	 gvec[i] += (moncoeff*cd[bigv]);
	 if (ends_with_c(deg,v,Fib)) // increase the full-monomial-counter
	    bigv += Fib[degfill+1];
	 else
	    bigv += Fib[degfill];
      }
   }

   Vector<int> hvec (d+1);
   hvec[0] = hvec[d] = 1;
   for (int i = 1; i <= d/2; ++i)
      hvec[d-i] = hvec[i] = gvec[i] + hvec[i-1];

   p.take("G_VECTOR") << gvec;
   p.take("H_VECTOR") << hvec;
}
} } // end namespace polymake::polytope


using namespace polymake;

/** @file toric_g_vector
 *  Calculate the toric g-vector and the (generalised) h-vector from the cd-index of a polytope.
 *  See
 *
 *      Richard Ehrenborg, Lifting inequalities for polytopes,
 *      Adv.Math. 193 (2005), 205-222, section 4
 *
 *  @synopsis toric_g_vector <file>
 *
 *  @reading  DIM, CD_INDEX_COEFFICIENTS
 *  @writing  G_VECTOR, H_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::toric_g_vector (p);
   }
   catch (const std::exception& e)
   {
      cerr << e.what() << endl;
      return 1;
   }
   return 0;
}
