########################################################################
#  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
#  of the License, or (at your option) any later version.
#  
#  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.
#  
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 51 Franklin Street, Fifth Floor,
#  Boston, MA  02110-1301, USA.
#  
#  ---
#  Copyright (C) 2011-2015, Simon Hampe <simon.hampe@googlemail.com>
#
#  ---
#  Copyright (c) 2016-2021
#  Ewgenij Gawrilow, Michael Joswig, and the polymake team
#  Technische Universität Berlin, Germany
#  https://polymake.org
#
#
#  Defines properties and rules for TropicalRationalFunction
########################################################################


# @Category Combinatorics
object TropicalRationalFunction {

   # Operator overload
   # Fallback=> TRUE is needed to that perl can implement missing operators such as == in the usual manner.
   use overload('*' => 'mult', '""' => 'to_string', bool => 'bool', '^' => 'power_operator', '+' => 'sum', '-' => 'minus', 'neg'=>'neg', fallback => 1);

   # @category Defining morphisms and functions
   # This property describes the affine linearity domains of the function. I.e. the function is
   # affine integral linear on each maximal polytope of [[DOMAIN]]. 
   property DOMAIN : Cycle<Addition>; 

   # @category Defining morphisms and functions
   # The value at index i describes the function value at [[DOMAIN]]->SEPARATED_VERTICES->row(i). 
   # More precisely, if the corresponding vertex is not a far ray, it describes its function value. 
   # If it is a directional ray, it describes the slope on that ray.
   property VERTEX_VALUES : Vector<Rational>;

   # @category Defining morphisms and functions 
   # The value at index i describes the function value of [[DOMAIN]]->LINEALITY_SPACE->row(i)
   property LINEALITY_VALUES : Vector<Rational>; 

   # @category Defining morphisms and functions
   # When representing the function as a quotient of tropical polynomials, this
   # is the numerator. Should be a homogeneous polynomial of the same degree as [[DENOMINATOR]].
   property NUMERATOR : Polynomial<TropicalNumber<Addition>>;

   # @category Defining morphisms and functions
   # When representing the function as a quotient of tropical polynomials, this
   # is the denominator. Should be a homogeneous polynomial of the same degree as [[NUMERATOR]]. 
   property DENOMINATOR : Polynomial<TropicalNumber<Addition>>;


   # @category Defining morphisms and functions
   # This is an internally used property that should not actually be set by the user. When creating a
   # rational function with the ^-operator, this property is set to the exponent. 
   # The semantics is that when computing a divisor, this function should be applied so many times
   # The usual application of this is a call to divisor($X, $f^4) or something similar.
   # Warning: This property is not stored if the TropicalRationalFunction object is saved. Nor should be assumed
   # to be preserved during any kind of arithmetic or restricting operation.
   property POWER : Int : non_storable;

   # @category Defining morphisms and functions
   # This is TRUE, if the function is defined on the full projective torus by a [[NUMERATOR]] and a 
   # [[DENOMINATOR]].
   # The rules do not actually check for completeness of the [[DOMAIN]]. This property will be set to true, 
   # if the function is created only via [[NUMERATOR]] and [[DENOMINATOR]]. Otherwise
   # it will be set to FALSE (or you can set it manually upon creation).
   property IS_GLOBALLY_DEFINED : Bool;

   ## RULES ###################

   rule POWER : {
      $this->POWER = 1;
   }

   rule DOMAIN.VERTICES, DOMAIN.MAXIMAL_POLYTOPES, DOMAIN.LINEALITY_SPACE, IS_GLOBALLY_DEFINED : NUMERATOR, DENOMINATOR {
      computeDomain($this);
      $this->IS_GLOBALLY_DEFINED = !(defined($this->lookup("VERTEX_VALUES")) || defined($this->lookup("LINEALITY_VALUES")));
   }
   weight 2.10;
   incurs DOMAIN.RaysPerm;
   #FIXME: THis property should maybe be used in some arithmetic functions...

   rule IS_GLOBALLY_DEFINED : {
      $this->IS_GLOBALLY_DEFINED = 0;
   }
   precondition : defined(DOMAIN);
   weight 0.1;
   #If a domain has been defined and this property is not true, it is false.

   rule LINEALITY_VALUES : {		
      $this->LINEALITY_VALUES = new Vector<Rational>();
   }
   precondition : DOMAIN.LINEALITY_SPACE {
      $this->DOMAIN->LINEALITY_SPACE->rows() == 0;
   }
   weight 0.10;

   rule VERTEX_VALUES, LINEALITY_VALUES : DOMAIN, NUMERATOR, DENOMINATOR {
      computeGeometricFunctionData($this);
   }
   precondition : NUMERATOR, DENOMINATOR {
      $this->NUMERATOR != new Polynomial<TropicalNumber<Addition>>($this->NUMERATOR->n_vars())
        and
      polynomial_degree($this->NUMERATOR) == polynomial_degree($this->DENOMINATOR)
        and
      is_homogeneous($this->NUMERATOR)
        and
      is_homogeneous($this->DENOMINATOR);
   }

   # @category Defining morphisms and functions
   # Computes the restriction of this TropicalRationalFunction on a given Cycle.
   # The cycle need not be contained in the [[DOMAIN]] of the function, the restriction
   # will be computed on the intersection of the cycle and the [[DOMAIN]].
   # @param Cycle<Addition> C The new domain.
   # @return TropicalRationalFunction<Addition>
   user_method restrict(Cycle<Addition>) {
      my ($f,$X) = @_;

      # Need to do that to be able to use it in rules of $X
      my $Xcopy = new Cycle<Addition>(VERTICES=>$X->VERTICES, MAXIMAL_POLYTOPES=>$X->MAXIMAL_POLYTOPES, 
                                      LINEALITY_SPACE=>$X->LINEALITY_SPACE, WEIGHTS=>$X->WEIGHTS);

      # Define identity on X
      my $vertexvalues = new Matrix<Rational>($Xcopy->SEPARATED_VERTICES->minor(All, range_from(1)));
      my $linealityvalues = new Matrix<Rational>($Xcopy->LINEALITY_SPACE->minor(All, range_from(1)));

      my $emb = new Morphism<Addition>( DOMAIN => $Xcopy,
                                        VERTEX_VALUES => $vertexvalues,
                                        LINEALITY_VALUES => $linealityvalues,
                                        MATRIX => unit_matrix<Rational>($f->DOMAIN->PROJECTIVE_AMBIENT_DIM+1)
                                      );
      return pullback($emb,$f);
   }

   # Changes the power of the function
   method to_the_power_of {
      my ($this,$k) = @_;
      if ($k < 0) {
         die "Illegal function power. Must be >= 0.";
      }
      if (defined($this->lookup("NUMERATOR"))) {
         if (defined($this->lookup("DOMAIN"))) {
            return new TropicalRationalFunction<Addition>(NUMERATOR=>$this->NUMERATOR, DENOMINATOR=>$this->DENOMINATOR,
                                                  DOMAIN=>$this->DOMAIN, POWER=>$k);
         } else {
            return new TropicalRationalFunction<Addition>(NUMERATOR=>$this->NUMERATOR, DENOMINATOR=>$this->DENOMINATOR,
                                                  POWER=>$k);
         }
      }
      return new TropicalRationalFunction<Addition>(DOMAIN=>$this->DOMAIN, VERTEX_VALUES=>$this->VERTEX_VALUES,
                                            LINEALITY_VALUES=>$this->LINEALITY_VALUES,POWER=>$k);
   }

   ## OPERATORS ########
	
   sub power_operator {
      my ($f,$k) = @_;
      return $f->to_the_power_of($k);
   }

   sub bool {
      my $this = shift;
      return !undef($this);
   }

   sub to_string {
      my $this = shift;
      if (defined($this->lookup("NUMERATOR"))) {
         return "(".$this->NUMERATOR.") / (".$this->DENOMINATOR.")";
      } else {
         return "Rational function on ".$this->DOMAIN->description();
      }
   }

   sub sum {
      my ($f,$g) = @_;
      if (!is_compatible($f, $g)) {
         die "Rational functions use different tropical additions.";
      }
      if (defined($f->lookup("NUMERATOR")) && defined($g->lookup("NUMERATOR"))) {
         return new TropicalRationalFunction<Addition>(NUMERATOR=>($f->NUMERATOR * $g->NUMERATOR),
                                               DENOMINATOR=>($f->DENOMINATOR * $g->DENOMINATOR));
      } else {
         return add_rational_functions($f,$g);
      }
   }

   sub minus {
      my ($f, $g) = @_;
      return sum($f, (-1)*$g);
   }

   #FIXME: Still need tropical sum?

   #Multiply by an integer
   sub mult {
      my ($this,$r) = @_;
      if ($r == 0) {
         my $zero;
         my $nvars;
         if (defined($this->lookup("NUMERATOR"))) {
            $nvars= $this->NUMERATOR->n_vars();
         } else {
            $ambient_dim = $this->DOMAIN->FAN_AMBIENT_DIM;
            $nvars = $ambient_dim;
         }
         $zero = new Polynomial<TropicalNumber<Addition>>(new TropicalNumber<Addition>(0), $nvars);
         return new TropicalRationalFunction<Addition>(NUMERATOR=>$zero, DENOMINATOR=>$zero);
      }
      if (defined($this->lookup("NUMERATOR"))) {
         my $num = new Polynomial<TropicalNumber<Addition>>($this->NUMERATOR);
         my $den = new Polynomial<TropicalNumber<Addition>>($this->DENOMINATOR);
         my $power = $r < 0? -$r : $r;
         for my $i (1 .. $power-1) {
            $num *= $num;
            $den *= $den;
         }
         return new TropicalRationalFunction<Addition>(NUMERATOR=>($r > 0? $num : $den), DENOMINATOR=>($r > 0? $den : $num));
      } else {
         return new TropicalRationalFunction<Addition>(DOMAIN=>$this->DOMAIN,VERTEX_VALUES=>$r*($this->VERTEX_VALUES),
                                               LINEALITY_VALUES=>$r*($this->LINEALITY_VALUES));
      }
   }

   sub neg {
      my $f = shift;
      return mult($f,-1);
   }
}

# @category Affine and projective coordinates
# This takes a tropical polynomial defined on tropical affine coordinates and turns it into 
# a rational function on tropical homogeneous coordinates
# @param Polynomial<TropicalNumber<Addition>> p A polynomial on affine coordinates.
# @param Int chart The index of the homogenizing coordinate. 0 by default.
# @return TropicalRationalFunction A rational function, which on the given chart is described by p.
user_function rational_fct_from_affine_numerator<Addition>(Polynomial<TropicalNumber<Addition>>; $=0) {
   my ($p, $chart) = @_;
   return homogenize_quotient($p,
                              new Polynomial<TropicalNumber<Addition>>(new TropicalNumber<Addition>(0), $p->n_vars()),
                              $chart);
}

# @category Affine and projective coordinates
# Same as [[rational_fct_from_affine_numerator]](Polynomial), except that it takes
# a string which it converts to a tropical polynomial using [[toTropicalPolynomial]].
# @param String p A string that will be converted to a tropical polynomial
# @param Int chart The index of the homogenizing coordinate. 0 by default.
# @return TropicalRationalFunction 
user_function rational_fct_from_affine_numerator(String; $=0) {
   my ($s, $chart) = @_;
   return rational_fct_from_affine_numerator(toTropicalPolynomial($s), $chart);
}

# @category Affine and projective coordinates
# This takes a tropical polynomial p defined on tropical affine coordinates and turns it into 
# the rational function (1/p) on tropical homogeneous coordinates
# @param Polynomial<TropicalNumber<Addition>> p A polynomial on affine coordinates.
# @param Int chart The index of the homogenizing coordinate. 0 by default.
# @return TropicalRationalFunction A rational function, which on the given chart is described by (1/p).
user_function rational_fct_from_affine_denominator<Addition>(Polynomial<TropicalNumber<Addition>>; $=0) {
   my ($p, $chart) = @_;
   return homogenize_quotient(new Polynomial<TropicalNumber<Addition>>(new TropicalNumber<Addition>(0), $p->n_vars()),
                              $p,
                              $chart);
}

# @category Affine and projective coordinates
# Same as [[rational_fct_from_affine_denominator]](Polynomial), except that it takes
# a string which it converts to a tropical polynomial using [[toTropicalPolynomial]].
# @param String p A string that will be converted to a tropical polynomial
# @param Int chart The index of the homogenizing coordinate. 0 by default.
# @return TropicalRationalFunction 
user_function rational_fct_from_affine_denominator(String; $=0) {
   my ($s, $chart) = @_;
   return rational_fct_from_affine_denominator(toTropicalPolynomial($s),$chart);
}

# @category Intersection theory
# Constructs a list of rational functions that cut out a single point in the projective torus
# @param Vector<Rational> A point in the projective torus, given in tropical homogeneous coordinates, but without
# leading coordinate.
# @tparam Addition Min or Max. Determines the type of the rational functions.
# @return TropicalRationalFunction. A perl array of rational functions of the form (v_i*x_0 + x_i)/(x_0), i = 1,..,n
user_function point_functions<Addition>($) {
   my $v = new Vector<Rational>(shift);
   my @vars = monomials<TropicalNumber<Addition> >($v->dim());
   my $denom = $vars[0];
   map {
      my $num = $vars[$_] + new TropicalNumber<Addition>($v->[$_] - $v->[0]) * $vars[0];
      new TropicalRationalFunction<Addition>(NUMERATOR=>$num, DENOMINATOR=>$denom);
   } 1 .. $v->dim()-1;
}


# Local Variables:
# mode: perl
# cperl-indent-level: 3
# indent-tabs-mode:nil
# End:
