/* 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: t_homology.cc,v 1.24 2005/10/20 09:03:10 witte Exp $"

#if #CoefficientType(vint)
#  include <vint.h>
   typedef pm::vint CoefficientType;
#else
#  include <Integer.h>
   typedef Integer CoefficientType;
#endif

#include <cstdlib>
#include <cctype>
#include <Poly.h>
#include <SimplicialComplex_as_FaceMap.h>
#include <ChainComplex.h>
#include <Array.h>
#include <argv_options.h>
#include <stopwatch.h>

namespace polymake { namespace topaz {

void homologies(Poly& p, const char* complex_section, const char* homology_section,
		int dim_high, int dim_low, bool co, stopwatch& timer)
{
   const SimplicialComplex_as_FaceMap<int> SC=p.give(complex_section);
   const int dim=p.give("DIM");
   if (dim<0)
     throw std::runtime_error("t_homology: DIM < 0");
   
#ifdef POLY_TIMING
   timer.start();
#endif

   typedef ChainComplex< CoefficientType, SimplicialComplex_as_FaceMap<int> > chain_complex;
   const chain_complex CC(SC,dim_high,dim_low);
   Array<chain_complex::homology_type> H(CC.size());

   if (!co)
      std::copy(entire(homologies(CC)), H.rbegin());
   else
      std::copy(entire(cohomologies(CC)), H.begin());

#ifdef POLY_TIMING
   timer.stop();
#endif

   p.take(homology_section) << H;
}

template <typename Complex, typename HomologyOutputIterator, typename CycleOutputIterator>
void store_homologies_and_cycles(const Complex& CC, const SimplicialComplex_as_FaceMap<int>& SC,
				 HomologyOutputIterator hom_it, CycleOutputIterator cycle_it)
{
   for (typename Complex::iterator chain_it=CC.begin();  !chain_it.at_end();  ++chain_it, ++hom_it, ++cycle_it) {
      *hom_it = *chain_it;

      const int n=chain_it.cycle_coeffs().cols() - empty_cols(chain_it.cycle_coeffs());
      cycle_it->coeffs.resize(chain_it.cycle_coeffs().rows(), n);
      typename Cols< typename Complex::cycle_group_type::coeff_matrix >::iterator coeff_it=cols(cycle_it->coeffs).begin();
      cycle_it->faces.resize(n);
      typename Complex::cycle_group_type::face_list::iterator cycle_face_it=cycle_it->faces.begin();
#ifdef POLY_DEBUG
      cout << "faces[" << chain_it.dim() << "]:\n";
#endif

      for (typename Entire< typename SimplicialComplex_as_FaceMap<int>::Faces_of_Dim >::iterator
	      face_it=entire(SC.faces_of_dim(chain_it.dim()));  !face_it.at_end();  ++face_it) {
	 if (! chain_it.cycle_coeffs().col(face_it.data()).empty()) {
#ifdef POLY_DEBUG
	    cout << face_it.data() << ": " << *face_it << endl;
#endif
	    *cycle_face_it = *face_it;  ++cycle_face_it;
	    *coeff_it = chain_it.cycle_coeffs().col(face_it.data());  ++coeff_it;
	 }
      }
   }
}

void homologies_and_cycles(Poly& p, const char* complex_section, const char* homology_section, const char *cycle_section,
			   int dim_high, int dim_low, bool co, stopwatch& timer)
{
   const SimplicialComplex_as_FaceMap<int> SC=p.give(complex_section);
   const int dim=p.give("DIM");
   if (dim<0)
     throw std::runtime_error("t_homology: DIM < 0");
   
#ifdef POLY_TIMING
   timer.start();
#endif

   typedef ChainComplex< CoefficientType, SimplicialComplex_as_FaceMap<int> > chain_complex;
   const chain_complex CC(SC,dim_high,dim_low);
   Array<chain_complex::homology_type> H(CC.size());
   Array<chain_complex::cycle_group_type> cycle_groups(CC.size());

   if (!co)
      store_homologies_and_cycles(homologies_and_cycles(CC), SC, H.rbegin(), cycle_groups.rbegin());
   else
      store_homologies_and_cycles(cohomologies_and_cocycles(CC), SC, H.begin(), cycle_groups.begin());

#ifdef POLY_TIMING
   timer.stop();
#endif

   p.take(homology_section) << H;
   p.take(cycle_section) << cycle_groups;
}

} }

using namespace polymake;

/** @file t_homology
 *
 *  Calculate the homology groups and, optionally, cycle representatives of a simplicial complex.
 *
 *  A narrower dimension range of interest can be specified.
 *  Negative values are treated as co-dimension - 1.
 *
 *  With option -co, cohomologies and cocycles will be computed.
 *
 *  @synopsis t_homology <file> <complex_section> <homology_section> [-co] [ <cycle_section> | <dim_high> <dim_low> ]
 *
 *  @reading  <complex_section>, DIM
 *  @writing  <homology_section>, <cycle_section>
 */

int main(int argc, const char* argv[])
{
   argv_option options[]={ { "-co" } };
   enum { co };
   if (!extract_options(argc,argv,1,options) || argc < 4 || argc > 7) {
      cerr << "usage: " << argv[0] << " <file> [ -co ] <complex_section> <homology_section> [ <cycle_section> ] [ <dim_high> <dim_low> ]" << endl;
      return 1;
   }
   const bool with_cycles=argc>4 && std::isalpha(*argv[4]);
   const int id=4+with_cycles;
   const int dim_high= argc>id ? atoi(argv[id]) : -1,
             dim_low= argc>id+1 ? atoi(argv[id+1]) : 0;
   try {
      Poly p(argv[1], ios::in|ios::out);
      stopwatch timer;
      if (with_cycles)
	 topaz::homologies_and_cycles(p, argv[2], argv[3], argv[4], dim_high, dim_low, options[co], timer);
      else
	 topaz::homologies(p, argv[2], argv[3], dim_high, dim_low, options[co], timer);
#ifdef POLY_TIMING
      cout << "time: " << timer << " seconds" << endl;
#endif
   } catch (const std::exception& e) {
      cerr << e.what() << endl;
      return 1;
   }
   return 0;
}
