/* Copyright (c) 2018
   Charles Jordan (Hokkaido University, Sapporo, Japan),
   Michael Joswig, Lars Kastner (Technische Universitaet Berlin, Germany)

   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 3, 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.
-----------------------------------------------------------------------------
*/

///////////////////////////////////////////////////////////////////////////////
// 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:
      const TriangNode triangulation;
      const gkz_type& gkz;
   public:
      const 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;
      const gkz_type& gkz;
      TriangNode optimal;
      triang_vector_type optimal_tvec;
      std::map<int, std::map<int, const gp_elem*>> group;
      void update_optimal(const NewTD& current){
         // For typedef pm::Set<int> triang_vector_type;
         // the correct relation here is ">".
         // If we used pm::Vector<int> instead, it should be "<".
         if(optimal_tvec > current.tvec){
            optimal_tvec = current.tvec;
            optimal = current.triangulation;
         }
      }
      void optimize(){
         NewTD tmp(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_in, const gkz_vector_type& s):
         ncr(n), optimal(t), optimal_tvec(gkz_in.triangulationAsVector(t)), sig(s), gkz(gkz_in)
      {}
      TriangNode get_optimal(){
         optimize();
         return optimal;
      }
      
      

};
#endif
