#  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: normaliz.rules 9502 2010-02-24 00:26:29Z gawrilow $

# path to normaliz
custom $normaliz;

# path to enormalz
custom $enormalz;

CONFIGURE : !normaliz2.rules

CONFIGURE {
    find_via_path($normaliz,"normaliz") &&
    find_via_path($enormalz,"enormalz");
}

label normaliz

# @topic credits/normaliz
# Normaliz, computing normalizations of affine semigroups
# Copyright by Winfried Bruns, Robert Koch, et. al.
# ftp://ftp.mathematik.uni-osnabrueck.de/pub/osm/kommalg/software/

object Polytope<Rational> {

    rule normaliz.npoints, normaliz.nhilbert: N_LATTICE_POINTS , N_HILBERT_BASIS : AMBIENT_DIM , VERTICES | POINTS {
        my $tempname=new Tempfile;
        # write vertices to tempfile
        normaliz_print_vert($tempname,$this);
        run_normaliz($this->AMBIENT_DIM, $tempname);
        
        my $done = normaliz_parse($tempname,$this,npoints=>1,nhilb=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    precondition : BOUNDED;
    weight 5.20;
    
    rule normaliz.nhilbert: N_HILBERT_BASIS : AMBIENT_DIM , VERTICES | POINTS {
        my $tempname=new Tempfile;
        # write vertices to tempfile
        normaliz_print_vert($tempname, $this);
        run_normaliz($this->AMBIENT_DIM, $tempname);
        
        my $done = normaliz_parse($tempname,$this,nhilb=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    precondition : POINTED;
    weight 5.20;
    
    rule normaliz.points, normaliz.nhilbert: LATTICE_POINTS , N_HILBERT_BASIS : AMBIENT_DIM , VERTICES | POINTS {
        my $tempname=new Tempfile;
        # write vertices to tempfile
        normaliz_print_vert($tempname, $this);
        run_normaliz($this->AMBIENT_DIM, $tempname);
        
        my $done = normaliz_parse($tempname,$this,points=>1,nhilb=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    precondition : BOUNDED;
    weight 5.30;

    rule normaliz.hilbert: HILBERT_BASIS : AMBIENT_DIM , VERTICES | POINTS {
        my $tempname=new Tempfile;
        # write vertices to tempfile
        normaliz_print_vert($tempname, $this);
        run_normaliz($this->AMBIENT_DIM, $tempname);
        
        my $done = normaliz_parse($tempname,$this,hilb=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    precondition : POINTED;
    weight 5.40;
}


object LatticePolytope {

    rule normaliz.normal, normaliz.npoints: NORMAL , LATTICE_VOLUME , N_LATTICE_POINTS , N_HILBERT_BASIS : AMBIENT_DIM , VERTICES | POINTS {
        my $tempname=new Tempfile;
        # write vertices to tempfile
        normaliz_print_vert($tempname,$this);
        run_normaliz($this->AMBIENT_DIM, $tempname);

        my $done = normaliz_parse($tempname,$this,npoints=>1,normvol=>1,nhilb=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    } 
    weight 5.20;
    # this will effectively override all rules defined above
    override : SUPER::normaliz.nhilbert;
    override : SUPER::normaliz.hilbert;

    rule normaliz.normal, normaliz.points: \
    NORMAL , LATTICE_VOLUME , LATTICE_POINTS , N_HILBERT_BASIS : AMBIENT_DIM , VERTICES | POINTS {
        my $tempname=new Tempfile;
        # write vertices to tempfile
        normaliz_print_vert($tempname, $this);
        run_normaliz($this->AMBIENT_DIM, $tempname);
        my $done = normaliz_parse($tempname,$this,points=>1,normvol=>1,nhilb=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    weight 5.30;
    
    rule normaliz.normal, normaliz.npoints, normaliz.hilbert: \
    NORMAL , LATTICE_VOLUME , HILBERT_BASIS , N_LATTICE_POINTS : AMBIENT_DIM , VERTICES | POINTS {
        my $tempname=new Tempfile;
        # write vertices to tempfile
        normaliz_print_vert($tempname, $this);
        run_normaliz($this->AMBIENT_DIM, $tempname);
        
        my $done = normaliz_parse($tempname,$this,npoints=>1,hilb=>1,normvol=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    weight 5.40;
    
    rule normaliz.normal, normaliz.npoints, normaliz.ehrhartpoly: \
    LATTICE_VOLUME , N_LATTICE_POINTS , N_HILBERT_BASIS , NORMAL , EHRHART_POLYNOMIAL_COEFF , H_STAR_VECTOR : AMBIENT_DIM , VERTICES | POINTS {
        my $tempname=new Tempfile;
        # write vertices to tempfile
        normaliz_print_vert($tempname,$this);
        run_normaliz($this->AMBIENT_DIM, "-h", $tempname);
        
        my $done = normaliz_parse($tempname,$this,npoints=>1,normvol=>1,ehr=>1,nhilb=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    weight 5.25;
    
    rule normaliz.normal, normaliz.npoints, normaliz.hilbert, normaliz.ehrhartpoly: \
    LATTICE_VOLUME , HILBERT_BASIS , N_LATTICE_POINTS , NORMAL , EHRHART_POLYNOMIAL_COEFF , H_STAR_VECTOR : AMBIENT_DIM , VERTICES | POINTS {
        my $tempname=new Tempfile;
        # write vertices to tempfile
        normaliz_print_vert($tempname,$this);
        run_normaliz($this->AMBIENT_DIM, "-h", $tempname);
        
        my $done = normaliz_parse($tempname,$this,npoints=>1,hilb=>1,normvol=>1,ehr=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    weight 5.45;
}


#   parameters:   $tempname             read $tempname.out
#                       $this                     object to store data
#                       %flags                   store these values     (npoints, points, hilb, normvol, ehr)
sub normaliz_parse {
    my ($tempname,$this,%flags) = @_;
    
    my $readpoints = 0;
    my $isnormal = 1;
    my $count = 0;
    my $nhilb = 0;
    my @hilb = ();
    my @latt = ();
    my $done=0;
    
    # parse outout
    open(my $outfile, "<$tempname.out")
        or die "can't open output file $tempname.out: $!";
    
    local $_;
    while (<$outfile>) {
        if ($readpoints == 1) {
            # read lattice points
            if (/^$/) {
                # all lattice points read
                $this->N_LATTICE_POINTS=$count if ($flags{npoints});
                
                $this->LATTICE_POINTS=\@latt if ($flags{points});
                
                $this->HILBERT_BASIS=\@hilb if ($flags{hilb});
                
                $this->NORMAL=$isnormal if ($flags{normvol});
                
                $done++;
                $readpoints=0;
            } elsif (/^\t*\s*1\s/) {
                # generator with 1
                if ($flags{points} || $flags{hilb}) {
                    my @entry = split;
                    push @latt, \@entry if ($flags{points});
                    push @hilb, \@entry if ($flags{hilb});
                }
                $count++;
            } else {
                # found generator with !=1 as first coordinate !
                if ($flags{hilb}) {
                    my @entry = split;
                    push @hilb, \@entry;
                }
                $isnormal = 0;
            }
        } elsif (($nhilb) = /^(\d+)\sgenerators\sof\sintegral\sclosure:\s$/) {
            # start reading hilbert basis
            $this->N_HILBERT_BASIS=$nhilb if($flags{nhilb});
            $readpoints=1;
        } elsif (my ($hv)=/^h-vector\s=\s(.*)$/) {
            $this->H_STAR_VECTOR=new Vector<Integer>(split(' ',$hv))  if ($flags{ehr});
            $done++;
        } elsif (my ($ep)=/^Hilbert\spoly.*\s:\s(.*)/) {
            # parse ehrhart polynomial coefficients
            $this->EHRHART_POLYNOMIAL_COEFF=new Vector<Rational>(split(' ',$ep)) if ($flags{ehr});
            $done++;
        } elsif (my ($vol) = /^multiplicity\s=\s*(\d+)$/) {
            $this->LATTICE_VOLUME = $vol if ($flags{normvol});
            $done++;
        }
    }
    close $outfile;
    
    return $done;
}

sub normaliz_print_vert {
    my ($tempname, $this) = @_;

    open(my $infile, ">$tempname.in")
        or die "can't create temporary file $tempname.in: $!";
    
    my $vert = eliminate_denominators($this->VERTICES | POINTS);
    
    # header
    print $infile $vert->rows, "\n", $this->AMBIENT_DIM + 1, "\n";
    # data
    print $infile $vert;
    # mode 0
    print $infile "0\n";
    close $infile;
}

# run normaliz / enormalz depending on dimension
sub run_normaliz {
        my $dim=shift;
        if ($dim < 5) {
            if ($Verbose::external) {
                dbg_print( "running normaliz: $normaliz @_" );
            }
            system($normaliz,@_);
        } else {
            if ($Verbose::external) {
                dbg_print( "running enormalz: $enormalz @_" );
            }
            system($enormalz,@_);
        }
        if ($? == -1) {
            die "failed to execute: $!\n";
        }
        elsif ($? & 127) {
            die ("normaliz/enormalz failed");
        }
    }


# Local Variables:
# mode: perl
# c-basic-offset:4
# End:
