use strict; 
use warnings;

use application "polytope";

my $n = 0;
foreach my $folder (glob("canonical_representative/*")){
   if(-d "$folder"){
      # if($folder !~ m/3d3/){
      treat_folder($folder);
      # }
   }
}

sub treat_folder {
   my($folder) = @_;
   print "FOLDER: $folder\n";
   foreach my $file (glob("$folder/*.test")){
      treat_file($file, $folder);
      $n++;
      if($n == 10){
         # die;
      }
   }
}

sub treat_file {
   my($file, $folder) = @_;
   my($no) = $file =~ m/\/([^\/]*)\.test/;
   my $entire = `cat $file`;
   my($inputfile) = $entire =~ m/"INPUTFILE" : "([^"]*)"/;
   my $input = `cat ../../$inputfile`;
   print "$file $no\n";
   my($points, $group, $triang) = $input =~ m/(\[\D*\[.*\]\D*\])\D*(\[\D*\[.*\]\D*\])\D*({\D*{.*}\D*})/s;
   defined($points) or die "Points not found for $file";
   defined($group) or die "Group not found for $file";
   defined($triang) or die "Triangulation not found for $file";
   $points = new Matrix<Rational>(eval $points);
   $triang = convert_triang($triang);
   my $sg = "";
   if($entire =~ m/--small-group/){
      $sg = "--small-group";
   }
   my ($triangOUT, $gkzOUT, $permOUT, $result) = get_lex_minimal($input, $sg);
   my $check = verify_solution($points, $group, $triang, $triangOUT, $gkzOUT, $permOUT);
   if($check) {
      build_testcase($folder, $result, $entire, $no);
   } else {
      die "$file did not result in valid testcase.";
   }
}

sub build_testcase {
   my($folder, $result, $entire, $no) = @_;
   $folder =~ s/canonical_representative/canonical_representative_min/;
   if(! -e $folder){
      `mkdir -p $folder`;
   }
   open(my $testfile, ">$folder/$no.test");
   $entire =~ s/BINARY /BINARY --lex-min /s;
   print $testfile $entire;
   close($testfile);
   open(my $resultfile, ">$folder/$no.OK");
   print $resultfile $result;
   close($resultfile);
}

sub convert_triang {
   my($triang) = @_;
   $triang =~ s/\{/\[/g;
   $triang =~ s/\}/\]/g;
   return new Array<Set<Int>>(eval $triang);
}

sub get_lex_minimal {
   my($input, $sg) = @_;
   open(my $file, ">/tmp/lexmin.dat");
   print $file $input;
   close($file);
   my $result = `../../build/Opt/bin/canonicalRepresentative --lex-min $sg < /tmp/lexmin.dat 2>/dev/null`;
   my ($triang, $gkz, $perm) = $result =~ m/({\D*{.*}\D*})\D*gkz: (\[.*\]) - perm: (\[.*\])/;
   $triang = convert_triang($triang);
   $gkz = new Vector<Rational>(eval $gkz);
   $perm = new Array<Int>(eval $perm);
   return($triang, $gkz, $perm, $result);
}

sub verify_solution {
   my($points, $group, $triangIN, $triangOUT, $gkzOUT, $permOUT) = @_;
   # my $G = linear_symmetries(ones_vector($points->rows) | $points);
   # $G = $G->PERMUTATION_ACTION;
   my $G = new group::PermutationAction(GENERATORS=>new Array<Array<Int>>(eval $group));
   # print "Other group size: ",$otherG->ALL_GROUP_ELEMENTS->size(),"\n";
   print "G size: ",$G->ALL_GROUP_ELEMENTS->size(),"\n";
   my $toutgkz = gkz_wrap($points, $triangOUT);
   my $check = 1;
   $check &= $toutgkz == $gkzOUT;
   $check or print "GKZ vector of output triangulation is wrong.";
   $check = 1;
   my $orbit = group::orbit($G, gkz_wrap($points, $triangIN));
   my $pmoptimal = $orbit->[0];
   $check &= $gkzOUT == $pmoptimal;
   if(!$check){
      foreach my $g (@{$G->ALL_GROUP_ELEMENTS}){
         if(group::action_inv($g, gkz_wrap($points, $triangIN)) == $pmoptimal){
            print "Group element could be: $g\n";
         }
      }
   }
   $check or die "Output gkz vector is not optimal.\nOUT: $gkzOUT\nPM:  $pmoptimal\n";
   $check &= $gkzOUT == group::action_inv($permOUT, gkz_wrap($points, $triangIN));
   $check or die "Group does not do the right thing on gkz vector.";
   my $permutedT = group::action<group::on_elements>($permOUT, $triangIN);
   $check &= (new Set<Set<Int>>($triangOUT) == new Set<Set<Int>>($permutedT));
   # print "TIN: $triangIN\nTOUT:$triangOUT\nperm:$permOUT\n permuted:$permutedT\n";
   $check or die "Permutation does not do the right thing.";
   return $check;
}

sub gkz_wrap {
   my($points, $triang) = @_;
   my $result = new Vector(zero_vector($points->rows()));
   foreach my $simplex (@$triang){
      my $vol = det($points->minor($simplex, All));
      $vol = $vol < 0 ? -$vol : $vol;
      foreach my $i (@$simplex){
         $result->[$i] += $vol;
      }
   }
   return $result;
}
