///////////////////////////////////////////////////////////////////////////////
// FixedGKZOptimizer.hh
// 
// The method in this file produce the lex optimal representative of a class of
// a triangulation while assuming the gkz vector is already optimal and hence
// keeping it fixed. We avoid computing the whole stabilizer by preprocessing
// the group.
///////////////////////////////////////////////////////////////////////////////
#ifndef FIXEDGKZOPTIMIZER_HH
#define FIXEDGKZOPTIMIZER_HH
#include "TriangNode.hh"
#include "Symmetry.hh"
#include "GKZ.hh"
#include "mptopcom_typedefs.hh"


class NewTD {
   public:
      TriangNode triangulation;
      const gkz_type& gkz;
   public:
      triang_vector_type tvec;
      NewTD(const TriangNode& t, const gkz_type& g):
         triangulation(t), gkz(g), tvec(g.triangulationAsVector(t))
      {};
      NewTD mutate(const Symmetry* s) const{
         return NewTD((*s)(triangulation), gkz);
      }
};

class NewTRO {
   private:
      const GroupDecomposer& ncr;
      const gkz_vector_type& sig;
      NewTD optimal;
      std::map<int, std::map<int, const gp_elem*>> group;
      void update_optimal(const NewTD& current){
         if(optimal.tvec < current.tvec){
            optimal.tvec = current.tvec;
            optimal.triangulation = current.triangulation;
         }
      }
      void optimize(){
         NewTD tmp(optimal.triangulation, optimal.gkz);
         ncr.get_stabilizer(group, sig);
         optimize_inner(0, tmp);
      }
      void optimize_inner(int fixed, const NewTD& current){
         if(fixed > ncr.get_depth()){
            update_optimal(current);
            return;
         } else {
            // std::cout << "Going inside." << std::endl;
            optimize_inner(fixed+1, current);
            const std::map<int, const gp_elem*>& gp(group[fixed]);
            bool foundOne = false;
            for(const auto& g : gp){
               int i = g.first;
               if(current.tvec.exists(i)){
                  NewTD next(current.mutate(g.second));
                  optimize_inner(fixed+1, next);
                  foundOne = true;
               }
            }
            if(!foundOne){
               for(const auto& g : gp){
                  NewTD next(current.mutate(g.second));
                  optimize_inner(fixed+1, next);
               }
            }
         }
      }
   public:
      NewTRO(const TriangNode& t, const GroupDecomposer& n, const gkz_type& gkz, const gkz_vector_type& s):
         ncr(n), optimal(t, gkz), sig(s)
      {}
      TriangNode get_optimal(){
         optimize();
         return optimal.triangulation;
      }
      
      

};
#endif
