/* 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: stiefel_whitney.cc,v 1.14 2004/10/26 18:57:00 gawrilow Exp $"

#include <Poly.h>
#include <argv_options.h>
#include <FiniteFields.h>
#include <complex_tools.h>
#include <SimplicialComplex_as_FaceMap.h>
#include <PowerSet.h>
#include <Array.h>
#include <linalg.h>

namespace polymake { namespace topaz {
  
namespace {

bool regular(const Set<int>& s, const Set<int>& t)
{
   Entire< Set<int> >::const_iterator i(entire(s));
   INDEX_CHECK(i.at_end(),"regular: s empty");
   Set<int>::const_iterator j(t.begin());
   INDEX_CHECK(j.at_end(),"regular: t empty");

   // check B_{-1}
   if (*j<*i) return false;
   INDEX_CHECK(*j!=*i,"regular: s not subset of t");
   ++i;                      // now index odd

   // check B_m
   while (!i.at_end()) {
      while (*j<*i) ++j;     // terminates since s subset of t
      INDEX_CHECK(*j!=*i,"regular: s not subset of t");

      ++i; ++j;              // now index even
      if (i.at_end()) {
	 // check B_p, if p is odd
	 if (!j.at_end()) return false;
	 else return true;
      }

      INDEX_CHECK(j.at_end(),"regular: s not subset of t");
      if (*i!=*j) return false;
      
      ++i;                    // now index odd
   }

   return true;
}

} // end unnamed namespace

void stiefel_whitney(std::ostream& os, Poly& p, const char* out_section,
		     const bool verbose, int low_d, int high_d,
		     const bool high_d_given)
{
   typedef SimplicialComplex_as_FaceMap<> sc_type;
   sc_type SC = p.give("FACETS");
   
   const int d(SC.dim());
   if (!high_d_given)  high_d = d;
   if (low_d<0)        low_d+=d+1;
   if (high_d<0)       high_d+=d+1;
   
   if (high_d < low_d || high_d > d) {
      std::ostringstream e;
      e << "stiefel_whitney: dim_low(" << low_d << "), dim_high("
	<< high_d << ") out of bounds" << endl;
      throw std::runtime_error(e.str());
   }
   
   Array< PowerSet<int> > omega_cycle(high_d-low_d+1);
   Array< PowerSet<int> >::iterator omega_cycle_k=omega_cycle.begin();
   SC.complete_faces(d,low_d);
   
   for (int k=low_d; k<=high_d; ++k, ++omega_cycle_k) {
      const int size_k(SC.size_of_dim(k));
      Array< Set<int> > face(size_k);
      if (verbose)
	 os << "f_" << k << "=" << size_k << ", regular pairs:" << std::endl;
      SparseVector<GF2> omega(size_k);
      for (int l=k; l<=d; ++l)
	 for (Entire<sc_type::Faces_of_Dim>::const_iterator t(entire(SC.faces_of_dim(l))); !t.at_end(); ++t) {
	    for (Entire< Subsets_of_k<sc_type::reference> >::const_iterator s(entire(all_subsets_of_k(*t,k+1))); !s.at_end(); ++s) {
	       if (regular(*s,*t)) {
		  if (verbose)
		     wrap(os) << " " << *s << *t << endl;
		  const int s_idx(SC[*s]);
		  if (face[s_idx].empty()) face[s_idx]=*s;
		  omega[s_idx]+=GF2(1);
	       }
	    }
	 }
      if (k!=d) {
	 SparseMatrix<GF2> delta_kk(SC.boundary_matrix<GF2>(k+1));
	 omega = reduce(delta_kk,omega);
      }
      
      accumulate_in(entire(select(face,indices(omega))), operations::add(), *omega_cycle_k);
      if (verbose)
	 wrap(os) << "omega_" << k << ":\n" << *omega_cycle_k << endl;
   }
   
   p.take(out_section) << omega_cycle;
}
   
} }

using namespace polymake;

/** @file stiefel_whitney
 *
 *  Computes Stiefel-Whitney classes of mod 2 Euler space (in particular, closed manifold).
 *  Use option "-v" to show regular pairs and cycles.
 *
 *  A narrower dimension range of interest can be specified.
 *  Negative values are treated as co-dimension - 1.
 *
 *  @synopsis stiefel_whitney <file> <out_section> [-v] [<dim_low> [<dim_high>]]
 *
 *  @reading  FACETS
 *  @writing  out_section
 */

int main(int argc, const char* argv[])
{
   argv_option options[]={ { "-v" } };
   enum { verbose };
   if (!extract_options(argc,argv,2,options) || argc>5) {
      cerr << "usage: " << argv[0] << " <file> <out_section> [-v] [<dim_low> [<dim_high>]]" << endl;
      return 1;
   }
   try {
      Poly p(argv[1], ios::in | ios::out);

      const bool high_d_given = argc>4;

      int low_d=argc>3 ? atoi(argv[3]) : 0;
      int high_d=high_d_given ? atoi(argv[4]) : 0;

      topaz::stiefel_whitney(std::cout, p, argv[2], options[verbose], low_d, high_d, high_d_given);
   }
   catch (const std::exception& e) {
     cerr << e.what() << endl;
     return 1;
   }
   return 0;
}


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