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

#include <cstdlib>
#include <cctype>
#include <Rational.h>
#include <Poly.h>
#include <Bitset.h>
#include <Set.h>
#include <ext/hash_map>
#include <Vector.h>
#include <Matrix.h>
#include <SimplicialComplex_as_FaceMap.h>
#include <ChainComplex.h>

namespace polymake { namespace topaz {
  
namespace {

typedef Rational coeff_type;
typedef Matrix<coeff_type> cup_type;
typedef Array<int> reordering_type;
typedef std::pair<int, int> signature_type;
typedef std::pair<int, signature_type> intersection_form_type;

bool pivot(int& k, const int i, const cup_type& M, const reordering_type& ind)
{
   const int n(M.rows());
   k=i;
   while (k<n && !M(ind[k],ind[k]))
      ++k;
   return (k<n);
}

bool non_zero(int& k, const int i, const cup_type& M, const reordering_type& ind)
{
   const int n(M.rows());
   k=i;
   while (k<n && !M(ind[k],ind[i]))
      ++k;
   return (k<n);
}

signature_type signature(cup_type& M)
{
   const int n=M.rows();
   reordering_type ind(n,entire(sequence(0,n)));

   int i(0), k, positive(0), negative(0);

   while (i<n) {
      if(pivot(k,i,M,ind)) {
	 // eliminate with pivot element on the diagonal
	 if (k!=i) std::swap(ind[i],ind[k]);
	 coeff_type p(M(ind[i],ind[i]));
	 
	 for (int j=i+1; j<n; ++j) {
	    coeff_type c(M(ind[j],ind[i])/p);
	    M[ind[j]]-=c*M[ind[i]];
	 }
	 for (int j=i+1; j<n; ++j)
	    M(ind[i],ind[j])=0;

	 if (p>0)
	    ++positive;
	 else
	    ++negative;

	 ++i;
      } else {
	 // diagonalize hyperbolic pairs
	 if (non_zero(k,i,M,ind)) { // i!=k
	    if (k!=i+1) std::swap(ind[i+1],ind[k]);

	    Vector<coeff_type> sum(M[ind[i]]+M[ind[i+1]]), diff(M[ind[i]]-M[ind[i+1]]);
	    M[ind[i]]=sum; M.col(ind[i])=sum; M[ind[i+1]]=diff; M.col(ind[i+1])=diff;
	    M(ind[i],ind[i])*=2; M(ind[i+1],ind[i+1])*=-2;
	 } else   // zero row/column
	    ++i;
	 
      }

   }
   
   return signature_type(positive,negative);
}

} // end unnamed namespace

void intersection_form(Poly& p, const char* cycle_section, const char* cocycle_section,
		       const char* intersection_form_section)
{
   typedef CycleGroup<Integer> cycle_type;
   const Array<cycle_type> Cycles = p.give(cycle_section);
   
   const int d(Cycles.size()-1);
   if (d%4!=0) {
      std::ostringstream e;
      e << "intersection_form: Dimension " << d << " not divisible by 4";
      throw std::runtime_error(e.str());
   }
   
   const cycle_type::face_list facets(Cycles[d].faces);
   const cycle_type::coeff_matrix signs(Cycles[d].coeffs);
   
   if (signs.rows() != 1) {
      std::ostringstream e;
      wrap(e) << "intersection_form: Expected exactly one top level homology class, found " << signs.rows();
      throw std::runtime_error(e.str());
   }
   
   Entire<cycle_type::face_list>::const_iterator f(entire(facets));
   Entire<cycle_type::coeff_matrix::row_type>::const_iterator s(entire(signs[0]));
   std_ext::hash_map<Bitset,Integer> SignedFacets;
   for ( ; !f.at_end() && !s.at_end(); ++f, ++s)
      SignedFacets[Bitset(*f)]=*s;
   
   if (!f.at_end() || !s.at_end()) {
      std::ostringstream e;
      wrap(e) << "intersection_form: Error in input section " << cycle_section;
      throw std::runtime_error(e.str());
   }
     
   const Array<cycle_type> CoCycles = p.give(cocycle_section);
   const cycle_type::face_list small_faces(CoCycles[d/2].faces);
   const cycle_type::coeff_matrix small_cocycles(CoCycles[d/2].coeffs);
   const int n(small_cocycles.rows());
   
   cup_type Cup(n,n);
   
   int parity=0; // until we are not convinced of the converse we assume that the intersection form is even
   
   int i(0);
   for (Entire< Rows<cycle_type::coeff_matrix> >::const_iterator c(entire(rows(small_cocycles)));
	!c.at_end(); ++c, ++i) {
      int j(0);
      for (Entire< Rows<cycle_type::coeff_matrix> >::const_iterator d(entire(rows(small_cocycles))); !d.at_end(); ++d, ++j) {
	 Integer cup_product(0);
	 for (Entire<cycle_type::coeff_matrix::row_type>::const_iterator x(entire(*c)); !x.at_end(); ++x) {
	    const Set<int> face_x(small_faces[x.index()]);
	    const Bitset bit_face_x(face_x);
	    for (Entire<cycle_type::coeff_matrix::row_type>::const_iterator y(entire(*d)); !y.at_end(); ++y) {
	       const Set<int> face_y(small_faces[y.index()]);
	       Bitset this_union(bit_face_x+Bitset(face_y));
	       if (face_x.back()==face_y.front() && SignedFacets.find(this_union)!=SignedFacets.end())
		  cup_product+=SignedFacets[this_union]*(*x)*(*y);
	    }
	 }
	 Cup(i,j)=cup_product;
	 if (i==j && cup_product%2)
	    parity=1;
      }
   }
   
#ifdef POLY_DEBUG
   for (int i=0; i<n; ++i)
      for (int j=0; j<i; ++j)
	 if (Cup(i,j) != Cup(j,i)) {
	    std::ostringstream err;
	    err << "resulting cup product matrix not symmetric: [" << i << "," << j << "]\n";
	    wrap(err) << Cup;
	    throw std::runtime_error(err.str());
	 }
#endif

   const intersection_form_type IF(parity, signature(Cup));
   p.take(intersection_form_section) << IF;
}
   
} }

using namespace polymake;

/** @file intersection_form
 *
 *  Calculate the parity and the signature of the intersection form of an oriented 4-manifold.
 *
 *  @synopsis intersection_form <file> <cycle_section> <cocycle_section> <intersection_form_section>
 *
 *  @reading  <cycle_section> <cocycle_section>
 *  @writing  <signature_section>
 */

int main(int argc, const char* argv[])
{
   if (argc != 5) {
      cerr << "usage: " << argv[0] << " <file> <cycle_section> <cocycle_section> <intersection_form_section>" << endl;
      return 1;
   }

   try {
      Poly p(argv[1], ios::in|ios::out);
      topaz::intersection_form(p, argv[2], argv[3], argv[4]);
   } catch (const std::exception& e) {
      cerr << e.what() << endl;
      return 1;
   }
   return 0;
}


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