#  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.
#-------------------------------------------------------------------------------

# An intermediate object wrapping the ideal on the Singular side and providing its methods.
declare property_type SingularIdeal : c++ (include => ["polymake/ideal/singularIdeal.h"]) {

   # @category Constructors
   # Construct a Singular ideal with monomial ordering given by a matrix.
   method construct(Array<Polynomial>, Matrix<Int>) : c++;

   # @category Constructors
   # Construct a Singular ideal with monomial ordering given by a vector.
   method construct(Array<Polynomial>, Vector<Int>) : c++;

   # @category Constructors
   # Construct a Singular ideal with monomial ordering given by its name (in the Singular notation).
   method construct(Array<Polynomial>, String) : c++;

   # @category Singular interface
   # Compute the Groebner basis.
   method groebner() : void : c++ ;

   # @category Singular interface
   # Compute the dimension of the ideal.
   method dim() : c++ ;

   # @category Singular interface
   # Check via saturation whether the ideal contains a monomial.
   method contains_monomial(Ring) : c++ ;

   # @category Singular interface
   # Compute the initial ideal. Depends on monomial ordering.
   method initial_ideal() : c++;

   # @category Singular interface
   # Compute the radical of the ideal.
   method radical() : c++ ;

   # @category Singular interface
   method division(Polynomial, Ring) : c++;

   # @category Singular interface
   method solve() : c++;

   # @category Singular interface
   # Reduce a polynomial modulo the ideal, i.e. check ideal membership.
   method reduce(Polynomial, Ring) : c++;

   # @category Singular interface
   method polynomials(Ring) : c++;

   # @category Singular interface
   # Compute the primary decomposition of the ideal.
   method primary_decomposition() : c++;

}

object Groebner {

   # @category Singular interface
   # Intermediate object wrapping the Singular objects, i.e. the ring with the monomial ordering and the ideal.
   property SINGULAR_IDEAL : SingularIdeal : non_storable;

   user_method reduce(Polynomial) {
      my ($self, $p) = @_;
      # TODO: Take care if ring is correct.
      return $self->SINGULAR_IDEAL->reduce($p, $p->get_ring());
   }

   user_method division(Polynomial) {
      my ($self, $p) = @_;
      # TODO: Take care if ring is correct.
      return $self->SINGULAR_IDEAL->division($p, $p->get_ring());
   }

}

object Ideal {

   rule GROEBNER.INITIAL_IDEAL : GROEBNER.SINGULAR_IDEAL, RING {
      my $i = $this->GROEBNER->SINGULAR_IDEAL->initial_ideal();
      $this->GROEBNER->INITIAL_IDEAL = new Ideal(GENERATORS=>$i->polynomials($this->RING), RING=>$this->RING);
   }

   rule GROEBNER.SINGULAR_IDEAL : GENERATORS, GROEBNER.ORDER_MATRIX {
      $this->GROEBNER->SINGULAR_IDEAL = new SingularIdeal($this->GENERATORS, $this->GROEBNER->ORDER_MATRIX);
      $this->GROEBNER->SINGULAR_IDEAL->groebner();
   }
   precondition : !ZERO;

   rule GROEBNER.SINGULAR_IDEAL : GENERATORS, GROEBNER.ORDER_VECTOR {
      $this->GROEBNER->SINGULAR_IDEAL = new SingularIdeal($this->GENERATORS, $this->GROEBNER->ORDER_VECTOR);
      $this->GROEBNER->SINGULAR_IDEAL->groebner();
   }
   precondition : !ZERO;

   rule GROEBNER.SINGULAR_IDEAL : GENERATORS, GROEBNER.ORDER_NAME {
      $this->GROEBNER->SINGULAR_IDEAL = new SingularIdeal($this->GENERATORS, $this->GROEBNER->ORDER_NAME);
      $this->GROEBNER->SINGULAR_IDEAL->groebner();
   }
   precondition : !ZERO;

   rule GROEBNER.BASIS : GROEBNER.SINGULAR_IDEAL, RING {
      $this->GROEBNER->BASIS = $this->GROEBNER->SINGULAR_IDEAL->polynomials($this->RING);
   }

   rule GROEBNER.INITIAL_IDEAL.GENERATORS, GROEBNER.INITIAL_IDEAL.RING, GROEBNER.BASIS : GENERATORS, GROEBNER.ORDER_MATRIX , RING {
      my $si;
      unless ( defined($si = $this->lookup("GROEBNER.SINGULAR_IDEAL"))) {
         $si = new SingularIdeal($this->GENERATORS, $this->GROEBNER->ORDER_MATRIX);
         $si->groebner();
      }
      $this->GROEBNER->BASIS = $si->polynomials($this->RING);
      my $i = $si->initial_ideal();
      $this->GROEBNER->INITIAL_IDEAL->GENERATORS = $i->polynomials($this->RING);
      $this->GROEBNER->INITIAL_IDEAL->RING = $this->RING;
   }
   precondition : !ZERO;


   rule GROEBNER.SINGULAR_IDEAL : GROEBNER.BASIS , GROEBNER.ORDER_MATRIX {
      $this->GROEBNER->SINGULAR_IDEAL = new SingularIdeal($this->GROEBNER->BASIS, $this->GROEBNER->ORDER_MATRIX);
      $this->GROEBNER->SINGULAR_IDEAL->groebner();
   }

   rule MONOMIAL : RING , GENERATORS {
      my $si;
      unless (defined($si = $this->lookup("GROEBNER"))) {
         $si = $this->GROEBNER(ORDER_NAME=>"dp",temporary);
      }
      my $result = 1;
      # One can easily show that it suffices to check whether the generators
      # of the initial ideal are contained in the ideal.
      foreach my $mon (@{$si->INITIAL_IDEAL->GENERATORS}){
         $result &&= ($si->reduce($mon)->trivial);
      }
      $this->MONOMIAL = $result;
   }

   rule DIM : RING , GENERATORS {
      my $si;
      unless (defined($si = $this->lookup("GROEBNER.SINGULAR_IDEAL"))) {
         $si = $this->GROEBNER(ORDER_NAME=>"dp",temporary)->SINGULAR_IDEAL;
      }

      $this->DIM = $si->dim();
   }

   user_method SOLVE : RING, GENERATORS {
     my $this = shift;
     my $si;
     unless (defined($si = $this->lookup("GROEBNER.SINGULAR_IDEAL"))) {
         $si = $this->GROEBNER(ORDER_NAME=>"dp",temporary)->SINGULAR_IDEAL;
     }
     $si->solve();
   }

   rule PRIMARY_DECOMPOSITION : RING, GENERATORS {
      my $ring = $this->RING;
      my $si;
      unless (defined($si = $this->lookup("GROEBNER.SINGULAR_IDEAL"))) {
         $si = $this->GROEBNER(ORDER_NAME=>"dp",temporary)->SINGULAR_IDEAL;
      }
      my @pd = $si->primary_decomposition();
      my @res;
      foreach my $id (@pd) {
         push @res, new Ideal(GENERATORS=>$id->polynomials($ring),RING=>$ring,PRIMARY=>1);
      }
      $this->PRIMARY_DECOMPOSITION = \@res;
   }

   rule RADICAL : RING, GENERATORS {
      my $si;
      unless (defined($si = $this->lookup("GROEBNER.SINGULAR_IDEAL"))) {
         $si = $this->GROEBNER(ORDER_NAME=>"dp",temporary)->SINGULAR_IDEAL;
      }
      my $radical = $si->radical();
      $this->RADICAL = new Ideal(GENERATORS=>$radical->polynomials($this->RING),RING=>$this->RING);
   }
}


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