/* Copyright (c) 1997-2015
	Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Berlin, Germany)
http://www.polymake.org

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.
--------------------------------------------------------------------------------
*/

/** @file TropicalArithmetic.h
  @brief Implementation of classes relevant for tropical computations.
  */

#ifndef POLYMAKE_TROPICAL_ARITHMETIC_H
#define POLYMAKE_TROPICAL_ARITHMETIC_H

#include "polymake/Polynomial.h"
#include "polymake/Rational.h"
#include "polymake/TropicalNumber.h"
#include "polymake/Array.h"
#include "polymake/Matrix.h"
#include "polymake/Vector.h"
#include "polymake/graph/hungarian_method.h"


namespace pm {
   namespace operations {
      template <typename Addition, typename Scalar>
      struct div_skip_zero {
         typedef TropicalNumber<Addition, Scalar> first_argument_type;
         typedef TropicalNumber<Addition, Scalar> second_argument_type;
         typedef const TropicalNumber<Addition, Scalar> result_type;
         result_type operator() (typename function_argument<first_argument_type>::type a, typename function_argument<second_argument_type>::type b) const {
            if (is_zero(b)) {
               if (is_zero(a)) return TropicalNumber<Addition, Scalar>::zero();
               else {
                  if (identical<Addition,Max>::value) return TropicalNumber<Addition, Scalar>(std::numeric_limits<Scalar>::infinity());
                  else return TropicalNumber<Addition, Scalar>::dual_zero();
               }
            }
            return a/b;
         }
         template <typename Iterator2>
         const first_argument_type& operator() (partial_left, typename function_argument<first_argument_type>::type a, const Iterator2&) const {
            if (is_zero(a)) return zero_value<TropicalNumber<Addition, Scalar> >();
            else {
               if (identical<Addition,Max>::value) return TropicalNumber<Addition, Scalar>::zero();
               else return TropicalNumber<Addition, Scalar>::dual_zero();
            }
         }

         template <typename Iterator1>
         const second_argument_type& operator() (partial_right, const Iterator1&, typename function_argument<second_argument_type>::type b) const {
            return zero_value<TropicalNumber<Addition, Scalar> >();
         }

      };
   }  }

namespace polymake {
   namespace operations {
      using pm::operations::div_skip_zero; }

   namespace tropical {

      /*
       * @brief coordinatewise tropical quotient of two vectors with special treatment for 
       * inf entries
       */

      template <typename Vector1, typename Vector2, typename Addition, typename Scalar>
      pm::LazyVector2<const Vector1&, const Vector2&, operations::div_skip_zero<Addition, Scalar> > 
      rel_coord(const GenericVector<Vector1, TropicalNumber<Addition, Scalar> > &point, 
                const GenericVector<Vector2, TropicalNumber<Addition, Scalar> > &apex) {
         return pm::LazyVector2<const Vector1&, const Vector2&, operations::div_skip_zero<Addition, Scalar> >(point.top(), apex.top());
      }

      /*
       * @brief compute a solution of a tropical linear equality as the tropical
       * nearest point projection on the tropical cone generated by the columns of the matrix
       * @param Matrix A 
       * @param Vector b
       * @return solution of the tropical linear equality if existent;
       * if there is no solution the result yields a 'nearest non-solution'
       */
      template <typename Addition, typename Scalar, typename MatrixTop, typename VectorTop>
      Vector<TropicalNumber<Addition, Scalar> > principal_solution(const GenericMatrix<MatrixTop, TropicalNumber<Addition, Scalar> > &A, const GenericVector<VectorTop, TropicalNumber<Addition, Scalar> > &b) {
         typedef TropicalNumber<Addition, Scalar> TNumber;
         int n(A.cols());
         Vector<TNumber> x(n);
         TNumber t_one(TNumber::one());
         for(typename pm::ensure_features<Cols <MatrixTop >, pm::cons<pm::end_sensitive, pm::indexed> >::const_iterator col=entire(ensure(cols(A.top()),(pm::indexed*)0)); !col.at_end(); col++) {
            x[col.index()] = t_one/accumulate(rel_coord(*col, b.top()), operations::add());
         }
         return x;
      }


      template <typename Addition, typename Scalar, typename MatrixTop>
      TropicalNumber<Addition, Scalar> tdet(const GenericMatrix<MatrixTop, TropicalNumber<Addition,Scalar> >& matrix)
      {
         Scalar value(zero_value<Scalar>()); // empty matrix has tropical determinant zero
         const int d(matrix.rows());
         const Array<int > perm(graph::HungarianMethod<Scalar>(Addition::orientation()*Matrix<Scalar>(matrix.top())).stage());
         for(int k = 0; k < d; ++k) value += Scalar(matrix[k][perm[k]]);

         return TropicalNumber<Addition,Scalar>(value);
      }



} }

#endif // POLYMAKE_TROPICAL_ARITHMETIC_H

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