/* Copyright (c) 1997-2010
   Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Darmstadt, Germany)
   http://www.polymake.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.
--------------------------------------------------------------------------------
   $Project: polymake $$Id: lattice_tools.h 10033 2011-01-31 09:29:21Z paffenho $
*/

#ifndef POLYMAKE_POLYTOPE_LATTICE_TOOLS_H
#define POLYMAKE_POLYTOPE_LATTICE_TOOLS_H

#include "polymake/Matrix.h"
#include "polymake/Vector.h"
#include "polymake/linalg.h"

namespace polymake { namespace polytope {

inline
Vector<Integer> multiply_by_common_denominator(const Vector<Rational>& v)
{
   Vector<Integer> w(v.dim());
   if (v.dim()) {
      const Integer cd = common_denominator(v);
      Vector<Integer>::iterator r=w.begin();
      for (Entire< Vector<Rational> >::const_iterator s = entire(v); !s.at_end(); ++s, ++r) {
         *r = div_exact(cd, denominator(*s)) * numerator(*s);
      }
   }
   return w;
}

inline
Matrix<Integer> multiply_by_common_denominator(const Matrix<Rational>& M)
{
   Matrix<Integer> result(M.rows(), M.cols());
   if (M.cols() && M.rows()) {
      Rows< Matrix< Integer> >::iterator rit2 = rows(result).begin();
      for (Entire< Rows< Matrix<Rational> > >::const_iterator rit = entire(rows(M)); !rit.at_end(); ++rit, ++rit2)
         *rit2 = multiply_by_common_denominator(*rit);
   }
   return result;
}
    
template <typename Coord, typename _Vector>
inline
Vector<Coord> divide_by_gcd ( const GenericVector<_Vector,Coord> & v, 
                              typename pm::enable_if<void*, std::numeric_limits<Coord>::is_integer>::type=0 
                              )
{
   Vector<Coord> result(v.dim());
   if ( v.dim() ) 
      result = div_exact(v, gcd(v));
   return result;
}
      
template <typename Coord, typename _Matrix>
inline
Matrix<Coord> divide_by_gcd ( const GenericMatrix<_Matrix,Coord> & M,
                              typename pm::enable_if<void*, std::numeric_limits<Coord>::is_integer>::type=0 
                              )
{
   Matrix<Coord> result(M.rows(), M.cols());
   if (M.cols() && M.rows()) {
      typename Rows<Matrix<Coord> >::iterator rit2 = rows(result).begin();
      for (typename Entire< Rows<_Matrix > >::const_iterator rit = entire(rows(M)); !rit.at_end(); ++rit, ++rit2)
         *rit2 = div_exact(*rit, gcd(*rit));
   }
   return result;
}


template <typename Coord, typename _Matrix>
Matrix<Coord> primitive ( const GenericMatrix<_Matrix, Coord> & M ) {
   return divide_by_gcd(M);
}

template <typename Coord, typename _Matrix>
Matrix<Coord> primitive_affine ( const GenericMatrix<_Matrix, Coord> & M ) {
   return M.col(0)|divide_by_gcd(M.minor(All,~scalar2set(0)));
}

template <typename _Matrix>
Matrix<Integer> primitive ( const GenericMatrix<_Matrix, Rational> & M ) {
   Matrix<Integer> MI = multiply_by_common_denominator(M);
   return divide_by_gcd(MI);
}

template <typename _Matrix>
Matrix<Integer> primitive_affine ( const GenericMatrix<_Matrix, Rational> & M ) {
   Vector<Integer> first(M.rows());
   for ( int i = 0; i < M.rows(); ++i ) {
      Rational rfirst = M.row(i)[0];
      if ( numerator(rfirst) % denominator(rfirst) != 0 ) 
         throw std::runtime_error("homogeneous coordinates not integral");
      first[i] = div_exact(numerator(rfirst), denominator(rfirst));
   }
   Matrix<Integer> MI = multiply_by_common_denominator(M.minor(All,~scalar2set(0)));
   return first|divide_by_gcd(MI);
}

 
template <typename Coord, typename _Vector>
Vector<Coord> primitive ( const GenericVector<_Vector, Coord> & v ) {
   return divide_by_gcd(v);
}

template <typename Coord, typename _Vector>
Vector<Coord> primitive_affine ( const GenericVector<_Vector, Coord> & v ) {
   return v.slice(scalar2set(0))|divide_by_gcd(v.slice(~scalar2set(0)));
}

template <typename _Vector>
Vector<Integer> primitive ( const GenericVector<_Vector, Rational> & v ) {
   Vector<Integer> vi = multiply_by_common_denominator(v);
   return divide_by_gcd(vi);
}

template <typename _Vector>
Vector<Integer> primitive_affine ( const GenericVector<_Vector, Rational> & v ) {
   Rational rfirst = v.top()[0];
   if ( numerator(rfirst) % denominator(rfirst) != 0 ) 
      throw std::runtime_error("homogeneous coordinate not integral");
   Integer first = div_exact(numerator(rfirst),denominator(rfirst));
   Vector<Integer> vi = multiply_by_common_denominator(v.slice(~scalar2set(0)));
   return first|divide_by_gcd(vi);
}

   
} }

#endif // POLYMAKE_POLYTOPE_LATTICE_TOOLS_H

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