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

# path to norm64
custom $norm64;

# path to normbig
custom $normbig;

# additional parameters for calling normaliz2
# "-i": ignore setup.txt (default)
custom $normaliz2_param="-i";

CONFIGURE {
    (find_via_path($normbig,"normbig") &&
    find_via_path($norm64,"norm64") ) or return;
    
    if ($Verbose::external) {
        dbg_print( "testing norm64: $norm64 @_" );
    }
    open my $P, "$norm64 -i test 2>&1 |"
        or die "couldn't run 'norm64': $!";
    local $_;
    while (<$P>) {
        if (/Unknown option i/) {
            if ($Verbose::external) {
                dbg_print( "normaliz2 version 2.0 found" );
            }
            die <<'.';
polymake requires normaliz2 v2.1 or newer;
Please download the current version
and repeat the auto-configuration.
.
        }
    }
    return 1;
}

label normaliz2

# @topic credits/normaliz2
# Normaliz is a (command line) tool for computations in affine monoids, vector configurations, lattice polytopes,  and rational cones.
# Copyright by Winfried Bruns and Bogdan Ichim.
# http://www.math.uos.de/normaliz/

object Polytope<Rational> {

    rule normaliz2.npoints: N_LATTICE_POINTS : AMBIENT_DIM , VERTICES | POINTS {
        my $tempname=new Tempfile;
        # write vertices to tempfile
        normaliz2_print_vert($tempname,$this);
        run_normaliz2($tempname);
        
        my $done = normaliz2_parse($tempname,$this,npoints=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    precondition : BOUNDED;
    weight 5.15;

    rule normaliz2.npoints: N_LATTICE_POINTS : AMBIENT_DIM , FACETS | INEQUALITIES {
        my $tempname=new Tempfile;
        # write facets to tempfile
        normaliz2_print_ineq($tempname,$this);
        run_normaliz2($tempname);
        
        my $done = normaliz2_parse($tempname,$this,npoints=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    precondition : AMBIENT_DIM , DIM { $this->AMBIENT_DIM == $this->DIM }
    precondition : BOUNDED;
    weight 5.15;
    
    rule normaliz2.points: LATTICE_POINTS : AMBIENT_DIM , VERTICES | POINTS {
        my $tempname=new Tempfile;
        # write vertices to tempfile
        normaliz2_print_vert($tempname,$this);
        run_normaliz2($tempname);
        
        my $done = normaliz2_parse($tempname,$this,points=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    precondition : BOUNDED;
    weight 5.20;

    rule normaliz2.points: LATTICE_POINTS : AMBIENT_DIM , FACETS | INEQUALITIES {
        my $tempname=new Tempfile;
        # write facets to tempfile
        normaliz2_print_ineq($tempname,$this);
        run_normaliz2($tempname);
        
        my $done = normaliz2_parse($tempname,$this,points=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    precondition : AMBIENT_DIM , DIM { $this->AMBIENT_DIM == $this->DIM }
    precondition : BOUNDED;
    weight 5.20;
    
    
    rule normaliz2.npoints, normaliz2.nhilbert: \
    N_LATTICE_POINTS , N_HILBERT_BASIS : AMBIENT_DIM , VERTICES | POINTS {
        my $tempname=new Tempfile;
        # write vertices to tempfile
        normaliz2_print_vert($tempname,$this);
        run_normaliz2($tempname);
        
        my $done = normaliz2_parse($tempname,$this,npoints=>1,nhilb=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    precondition : BOUNDED;
    weight 5.20;
    
    rule normaliz2.npoints, normaliz2.nhilbert: \
    N_LATTICE_POINTS , N_HILBERT_BASIS : AMBIENT_DIM , FACETS | INEQUALITIES {
        my $tempname=new Tempfile;
        # write facets to tempfile
        normaliz2_print_ineq($tempname,$this);
        run_normaliz2($tempname);
        
        my $done = normaliz2_parse($tempname,$this,npoints=>1,nhilb=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    precondition : AMBIENT_DIM , DIM { $this->AMBIENT_DIM == $this->DIM }
    precondition : BOUNDED;
    weight 5.20;
    
    rule normaliz2.points, normaliz2.nhilbert: \
    LATTICE_POINTS , N_HILBERT_BASIS : AMBIENT_DIM , VERTICES | POINTS {
        my $tempname=new Tempfile;
        # write vertices to tempfile
        normaliz2_print_vert($tempname,$this);
        run_normaliz2($tempname);
        
        my $done = normaliz2_parse($tempname,$this,points=>1,nhilb=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    precondition : BOUNDED;
    weight 5.30;
    
    rule normaliz2.points, normaliz2.nhilbert: \
    LATTICE_POINTS , N_HILBERT_BASIS : AMBIENT_DIM , FACETS | INEQUALITIES {
        my $tempname=new Tempfile;
        # write facets to tempfile
        normaliz2_print_ineq($tempname,$this);
        run_normaliz2($tempname);
        
        my $done = normaliz2_parse($tempname,$this,points=>1,nhilb=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    precondition : AMBIENT_DIM , DIM { $this->AMBIENT_DIM == $this->DIM }
    precondition : BOUNDED;
    weight 5.30;
    
    
    rule normaliz2.nhilbert: N_HILBERT_BASIS : AMBIENT_DIM , VERTICES | POINTS {
        my $tempname=new Tempfile;
        # write vertices to tempfile
        normaliz2_print_vert($tempname,$this);
        run_normaliz2($tempname);
        
        my $done = normaliz2_parse($tempname,$this,nhilb=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    precondition : POINTED;
    weight 5.20;
    
    rule normaliz2.nhilbert: N_HILBERT_BASIS : AMBIENT_DIM , FACETS | INEQUALITIES {
        my $tempname=new Tempfile;
        # write facets to tempfile
        normaliz2_print_ineq($tempname,$this);
        run_normaliz2($tempname);
        
        my $done = normaliz2_parse($tempname,$this,nhilb=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    precondition : AMBIENT_DIM , DIM { $this->AMBIENT_DIM == $this->DIM }
    precondition : POINTED;
    weight 5.20;
    
    rule normaliz2.hilbert: HILBERT_BASIS : AMBIENT_DIM , VERTICES | POINTS {
        my $tempname=new Tempfile;
        # write vertices to tempfile
        normaliz2_print_vert($tempname,$this);
        run_normaliz2($tempname);
        
        my $done = normaliz2_parse($tempname,$this,hilb=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    precondition : POINTED;
    weight 5.40;
    
    rule normaliz2.hilbert: HILBERT_BASIS : AMBIENT_DIM , FACETS | INEQUALITIES {
        my $tempname=new Tempfile;
        # write facets to tempfile
        normaliz2_print_ineq($tempname,$this);
        run_normaliz2($tempname);
        
        my $done = normaliz2_parse($tempname,$this,hilb=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    precondition : AMBIENT_DIM , DIM { $this->AMBIENT_DIM == $this->DIM }
    precondition : POINTED;
    weight 5.40;
    
}


object LatticePolytope {
    
    rule normaliz2.ehrhartpoly, normaliz2.npoints: \
    LATTICE_VOLUME , EHRHART_POLYNOMIAL_COEFF , H_STAR_VECTOR , N_LATTICE_POINTS : AMBIENT_DIM , VERTICES | POINTS {
        my $tempname=new Tempfile;
        # write vertices to tempfile
        normaliz2_print_vert($tempname,$this);
        run_normaliz2("-p", $tempname);
        
        my $done = normaliz2_parse($tempname,$this,volume=>1,ehr=>1,npoints=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    weight 5.15;
    # this will effectively override all rules defined above for Polytope<Rational> 
    override : SUPER::normaliz2.npoints;
    override : SUPER::normaliz2.points;
    override : SUPER::normaliz2.nhilbert;
    override : SUPER::normaliz2.hilbert;
    
    rule normaliz2.ehrhartpoly, normaliz2.npoints: \
    LATTICE_VOLUME , EHRHART_POLYNOMIAL_COEFF , H_STAR_VECTOR , N_LATTICE_POINTS : AMBIENT_DIM , FACETS | INEQUALITIES {
        my $tempname=new Tempfile;
        # write facets to tempfile
        normaliz2_print_ineq($tempname,$this);
        run_normaliz2("-p", $tempname);
        
        my $done = normaliz2_parse($tempname,$this,volume=>1,ehr=>1,npoints=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    precondition : AMBIENT_DIM , DIM { $this->AMBIENT_DIM == $this->DIM }
    weight 5.15;
    
    rule normaliz2.ehrhartpoly, normaliz2.points: \
    LATTICE_VOLUME , EHRHART_POLYNOMIAL_COEFF , H_STAR_VECTOR , LATTICE_POINTS : AMBIENT_DIM , VERTICES | POINTS {
        my $tempname=new Tempfile;
        # write vertices to tempfile
        normaliz2_print_vert($tempname,$this);
        run_normaliz2("-p", $tempname);
        
        my $done = normaliz2_parse($tempname,$this,volume=>1,ehr=>1,points=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    weight 5.20;
    
    rule normaliz2.ehrhartpoly, normaliz2.points: \
    LATTICE_VOLUME , EHRHART_POLYNOMIAL_COEFF , H_STAR_VECTOR , LATTICE_POINTS : AMBIENT_DIM , FACETS | INEQUALITIES {
        my $tempname=new Tempfile;
        # write facets to tempfile
        normaliz2_print_ineq($tempname,$this);
        run_normaliz2("-p", $tempname);
        
        my $done = normaliz2_parse($tempname,$this,volume=>1,ehr=>1,points=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    precondition : AMBIENT_DIM , DIM { $this->AMBIENT_DIM == $this->DIM }
    weight 5.20;
    
    
    rule normaliz2.npoints, normaliz2.normal, normaliz2.nhilbert: \
    NORMAL , LATTICE_VOLUME , N_LATTICE_POINTS , N_HILBERT_BASIS : AMBIENT_DIM , VERTICES | POINTS {
        my $tempname=new Tempfile;
        # write vertices to tempfile
        normaliz2_print_vert($tempname,$this);
        run_normaliz2($tempname);
        
        my $done = normaliz2_parse($tempname,$this,npoints=>1,volume=>1,norm=>1,nhilb=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    weight 5.20;

    rule normaliz2.npoints, normaliz2.normal, normaliz2.nhilbert: \
    NORMAL , LATTICE_VOLUME , N_LATTICE_POINTS , N_HILBERT_BASIS : AMBIENT_DIM , FACETS | INEQUALITIES {
        my $tempname=new Tempfile;
        # write facets to tempfile
        normaliz2_print_ineq($tempname,$this);
        run_normaliz2($tempname);
        
        my $done = normaliz2_parse($tempname,$this,npoints=>1,volume=>1,norm=>1,nhilb=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    precondition : AMBIENT_DIM , DIM { $this->AMBIENT_DIM == $this->DIM }
    weight 5.20;


    rule normaliz2.npoints, normaliz2.ehrhartpoly, normaliz2.normal, normaliz2.nhilbert: \
    NORMAL , LATTICE_VOLUME , N_LATTICE_POINTS , N_HILBERT_BASIS, EHRHART_POLYNOMIAL_COEFF , \
    H_STAR_VECTOR : AMBIENT_DIM , VERTICES | POINTS {
        my $tempname=new Tempfile;
        # write vertices to tempfile
        normaliz2_print_vert($tempname,$this);
        run_normaliz2("-h", $tempname);
        
        my $done = normaliz2_parse($tempname,$this,npoints=>1,volume=>1,norm=>1,ehr=>1,nhilb=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    weight 5.25;

    rule normaliz2.npoints, normaliz2.ehrhartpoly, normaliz2.normal, normaliz2.nhilbert: \
    NORMAL , LATTICE_VOLUME , N_LATTICE_POINTS , N_HILBERT_BASIS, EHRHART_POLYNOMIAL_COEFF , \
    H_STAR_VECTOR : AMBIENT_DIM , FACETS | INEQUALITIES {
        my $tempname=new Tempfile;
        # write facets to tempfile
        normaliz2_print_ineq($tempname,$this);
        run_normaliz2("-h", $tempname);
        
        my $done = normaliz2_parse($tempname,$this,npoints=>1,volume=>1,norm=>1,ehr=>1,nhilb=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    precondition : AMBIENT_DIM , DIM { $this->AMBIENT_DIM == $this->DIM }
    weight 5.25;


    rule normaliz2.points, normaliz2.normal, normaliz2.nhilbert: \
    LATTICE_POINTS , N_HILBERT_BASIS, NORMAL , LATTICE_VOLUME : AMBIENT_DIM , VERTICES | POINTS {
        my $tempname=new Tempfile;
        # write vertices to tempfile
        normaliz2_print_vert($tempname,$this);
        run_normaliz2($tempname);
        
        my $done = normaliz2_parse($tempname,$this,points=>1,volume=>1,norm=>1,nhilb=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    weight 5.30;
    
    rule normaliz2.points, normaliz2.normal, normaliz2.nhilbert: \
    LATTICE_POINTS , N_HILBERT_BASIS, NORMAL , LATTICE_VOLUME : AMBIENT_DIM , FACETS | INEQUALITIES {
        my $tempname=new Tempfile;
        # write facets to tempfile
        normaliz2_print_ineq($tempname,$this);
        run_normaliz2($tempname);
        
        my $done = normaliz2_parse($tempname,$this,points=>1,volume=>1,norm=>1,nhilb=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    precondition : AMBIENT_DIM , DIM { $this->AMBIENT_DIM == $this->DIM }
    weight 5.30;


    rule normaliz2.npoints, normaliz2.hilbert, normaliz2.normal: \
    HILBERT_BASIS , N_LATTICE_POINTS , NORMAL , LATTICE_VOLUME : AMBIENT_DIM , VERTICES | POINTS {
        my $tempname=new Tempfile;
        # write vertices to tempfile
        normaliz2_print_vert($tempname,$this);
        run_normaliz2($tempname);
        
        my $done = normaliz2_parse($tempname,$this,npoints=>1,hilb=>1,volume=>1,norm=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    weight 5.40;
    
    rule normaliz2.npoints, normaliz2.hilbert, normaliz2.normal: \
    HILBERT_BASIS , N_LATTICE_POINTS , NORMAL , LATTICE_VOLUME : AMBIENT_DIM , FACETS | INEQUALITIES {
        my $tempname=new Tempfile;
        # write facets to tempfile
        normaliz2_print_ineq($tempname,$this);
        run_normaliz2($tempname);
        
        my $done = normaliz2_parse($tempname,$this,npoints=>1,hilb=>1,volume=>1,norm=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    precondition : AMBIENT_DIM , DIM { $this->AMBIENT_DIM == $this->DIM }
    weight 5.40;

    rule normaliz2.npoints, normaliz2.hilbert, normaliz2.ehrhartpoly, normaliz2.normal: \
    HILBERT_BASIS , N_LATTICE_POINTS , NORMAL , LATTICE_VOLUME , EHRHART_POLYNOMIAL_COEFF , \
    H_STAR_VECTOR : AMBIENT_DIM , VERTICES | POINTS {
        my $tempname=new Tempfile;
        # write vertices to tempfile
        normaliz2_print_vert($tempname,$this);
        run_normaliz2("-h", $tempname);
        
        my $done = normaliz2_parse($tempname,$this,npoints=>1,hilb=>1,volume=>1,norm=>1,ehr=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    weight 5.45;

    rule normaliz2.npoints, normaliz2.hilbert, normaliz2.ehrhartpoly, normaliz2.normal: \
    HILBERT_BASIS , N_LATTICE_POINTS , NORMAL , LATTICE_VOLUME , EHRHART_POLYNOMIAL_COEFF , \
    H_STAR_VECTOR : AMBIENT_DIM , FACETS | INEQUALITIES {
        my $tempname=new Tempfile;
        # write facets to tempfile
        normaliz2_print_ineq($tempname,$this);
        run_normaliz2("-h", $tempname);
        
        my $done = normaliz2_parse($tempname,$this,npoints=>1,hilb=>1,volume=>1,norm=>1,ehr=>1);
        if ($done == 0) {
            die "could not parse $tempname.out";
        }
    }
    precondition : AMBIENT_DIM , DIM { $this->AMBIENT_DIM == $this->DIM }
    weight 5.45;
    
}
    
    
#   parameters:   $tempname             read $tempname.out
#                 $this                 object to store data
#                 %flags                store these values     (npoints, points, hilb, volume, norm, ehr)
sub normaliz2_parse {
    my ($tempname,$this,%flags) = @_;
    
    my $readpoints = 0;
    my $readpoints2 = 0;
    my $pointsdone = 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 hilbert basis
            if (/^$/) {
                # all generators read
                $this->LATTICE_POINTS=\@latt if ($flags{points});
                $pointsdone = 1;
                
                $this->HILBERT_BASIS=\@hilb if ($flags{hilb});
                
                $this->NORMAL=$isnormal if ($flags{norm});
                
                $this->N_LATTICE_POINTS=$count if ($flags{npoints});
                
                $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 ($readpoints2 == 1) {
            if (/^$/) {
                $this->LATTICE_POINTS=\@latt if ($flags{points});
                $pointsdone=1;
                
                $done++;
                
                $readpoints2 = 0;
            } else {
                if ($flags{points}) {
                    my @entry = split;
                    push @latt, \@entry;
                }
            }
        } elsif (($nhilb) = /^(\d+)\sgenerators\sof\sintegral\sclosure:\s$/) {
            # start reading hilbert basis
            if ( ($flags{points} && !$pointsdone) || $flags{hilb} || $flags{norm} || (!$this->lookup("LATTICE")&& !$this->isa("LatticePolytope") && $flags{npoints})) {
                $readpoints=1;
            }
            $this->N_HILBERT_BASIS=$nhilb if ($flags{nhilb});
            $done++;
        } elsif ((($count) = /^(\d+)\sheight\s1\sgenerators\sof\sintegral\sclosure:/) && ($this->lookup("LATTICE") || $this->isa("LatticePolytope") )) {
            # start reading lattice points
            if ($flags{points} && !$pointsdone) {
                $readpoints2=1;
            }
            $this->N_LATTICE_POINTS=$count if ($flags{npoints});
            $done++;
        } 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{volume});
            $done++;
        }
    }
    close $outfile;
    
    return $done;
}

sub normaliz2_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;
}

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

    open(my $infile, ">$tempname.in")
        or die "can't create temporary file $tempname.in: $!";
    
    my $ineq = eliminate_denominators($this->FACETS | INEQUALITIES);
    my $n_ineq = $ineq->rows;
    my $n_lines = $n_ineq;
    my $ah;
    if (defined (my $AH = $this->lookup("AFFINE_HULL | EQUATIONS"))) {
        $n_lines = $n_lines + ($AH->rows);
        $ah = eliminate_denominators($AH);
    }
    
    # header
    print $infile $n_lines, "\n", $this->AMBIENT_DIM + 1, "\n";
    # data
    print $infile $ineq;
    if ($n_lines > $n_ineq) {
        print $infile $ah;
        print $infile (-$ah);
    }
    
    # mode 4
    print $infile "4\n";
    close $infile;
}

# run normaliz / enormalz depending on dimension
sub run_normaliz2 {
        if ($Verbose::external) {
            dbg_print( "running norm64: $norm64 $normaliz2_param @_" );
        }
        open my $P, "$norm64 $normaliz2_param @_ 2>&1 |"
            or die "couldn't run 'norm64': $!";
        local $_;
        while (<$P>) {
            if (/Po(s+)ible arithmetic overflow/ || /Some error detected/ || /bad format of the input file/) {
                if ($Verbose::external) {
                    dbg_print( "norm64 failed: $_" );
                    dbg_print( "running normbig: $normbig $normaliz2_param @_" );
                }
                system($normbig,$normaliz2_param,@_);
                if ($? == -1) {
                    die "failed to execute: $!\n";
                }
                elsif ($? & 127) {
                    die ("normbig failed");
                }
            }
        }
    }

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