use application 'polytope';
use Benchmark qw(:all);


# prints a progressbar
# @param Int got : the amount of stuff you have already got
# @param Int total : the total amount of stuff
# @param String text : additional text behind the bar.
sub progress_bar {
    my ( $got, $total, $text) = @_;
    my $width = 10;
    my $char = '=';
    local $| = 1;
    printf "|%-${width}s| $got/$total | %-80s \r",
    $char x (($width)*$got/$total). '>', $text;
}


# Creates a list of polytopes by removing one vertex and taking
# the convex hull over all the remaining lattice points.
# @param Set<Matrix<Rational>> input_polys a set containing all the vertices of the input polytopes
# @return Set<Matrix<Rational>> ouput_polys a set containing all the vertices of the ouput polytopes,
#                           but with one vertex removed
sub subpolylist_step($) {
my ($input_polys) = @_;

# variables for progress bar and timing
my $numPolys = $input_polys->size;
my $date=localtime(time);
print "subpolylist: started $date\n";
my $t0=Benchmark->new;

# Initializing the output set
my $output_polys = new Set<Matrix<Rational>>();

# For every polytope do:
#for (my $l=0; $l<scalar(@input_polys); ++$l){
my $l=0;
foreach my $vertices (@{$input_polys}){
    ++$l;
    # calc the lattice points
    my $tmpPoly = new Polytope(POINTS=>$vertices);
    prefer_now "projection"; my $latticePoints= new Set<Vector>(rows($tmpPoly->LATTICE_POINTS));
    progress_bar($l,$numPolys, "start removing vertices -- $date");

    # For every vertex do:
    my $num_verts = $vertices->rows();
    my $vert_i = 0;
    foreach my $v (@{rows($vertices)}){
	# progress bar printing
	++$vert_i;
	$date=localtime(time);
	progress_bar($l,$numPolys, "removing vertex $vert_i from $num_verts -- $date");	

	# create the convex hull over all lattice points
	# but without this vertex and check if [[LATTICE_WIDTH]] is 1
	my $tmpPoly = new LatticePolytope(POINTS=>($latticePoints-$v), BOUNDED=>1);
	next if (!$tmpPoly->FEASIBLE || $tmpPoly->LATTICE_WIDTH <= 1);

	# calc the affine lattice normal form and add the polytope
	# to our list (if it is not already there)
	my $outVertNorm = new Matrix(affine_lattice_normal_form($tmpPoly));
          # here we add $vertTnorm to the set
	  # but since we deal with sets, we do not take care of douplicates
	$output_polys += $outVertNorm;
    }	
}

# timing stops
my $t1=Benchmark->new;
my $t=timediff($t1,$t0);
printf "subpolylist: stopped %-80s\n", "$date";
print "  time: ".timestr($t)."\n";

# making the hash to a perl array
return $output_polys;
}

# Creates all lists of polytopes by successive removing one vertex and taking
# the convex hull over all the remaining lattice points.
# @param Set<Matrix<Rational>> input_polys a set containing all the vertices of the input polytopes.
#                                          They need to have the same number of lattice points
# @param Set<Matrix<Rational>> additional a set containing some polytopes which should be added at
#                                         the right time
# @param String path the path where all the lists should be saved
# @return void
sub subpolylist($$$){
    my ($list, $add_list, $path) = @_;

    # prepare a hash, with the polytopes associated to the number of lattice points
    my %hash = ();
    foreach my $verts (@{$add_list}){
	my $p = new LatticePolytope(POINTS=>$verts, BOUNDED=>1);
	my $n_lattice = $p->N_LATTICE_POINTS;
	if(exists($hash{"$n_lattice"})){
	    $hash{"$n_lattice"} += $verts;
	}else{
	    $hash{"$n_lattice"} = new Set<Matrix<Rational>>($verts);
	}
    }

    # check with how many lattice points we start
    my $n_lattice = new Polytope(POINTS=>$list->[0], BOUNDED=>1);
    $n_lattice = new Integer($n_lattice->N_LATTICE_POINTS);

    # iterate till the end
    while($list->size != 0){
	print "\n=== $n_lattice -> ".($n_lattice-1)." ===\n";

	# check if there are some polytopes to add
	$list += $hash{"$n_lattice"} if(exists($hash{"$n_lattice"}));
	$list = subpolylist_step($list);
	--$n_lattice;

	save_data($list, $path."/".$n_lattice."_polys.data");
	print "found ".$list->size." polytopes.\n";
    }
}



#			if ($L->size!=(($L+=$T)->size)){    #TODO Check if element is in set.. 
#			$L+=$T;
#			my $matT = new Matrix($T);
#			my $polyT = new Polytope(POINTS=>$matT);
#			my @vertT = sort(@{$polyT->VERTICES});
#			my $vertT = new Matrix(\@vertT);
#			$LM = $LM/new Matrix<Matrix>([[$matT,$vertT]]);
#			}




