/**CFile****************************************************************

  FileName    [eSLIM.cpp]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Using Exact Synthesis with the SAT-based Local Improvement Method (eSLIM).]

  Synopsis    [Interface to the eSLIM package.]

  Author      [Franz-Xaver Reichl]
  
  Affiliation [University of Freiburg]

  Date        [Ver. 1.0. Started - April 2026.]

  Revision    [$Id: eSLIM.cpp,v 1.00 2025/04/14 00:00:00 Exp $]

***********************************************************************/


#include "utils.hpp"
#include "eSLIMMan.hpp"
#include "relationGeneration.hpp"
#include "selectionStrategies.hpp"
#include "windowMan.hpp"

#include "misc/util/abc_namespaces.h"
#include "opt/mfs/mfs.h"
#include "base/main/main.h"

#include "eSLIM.h"
#include "eslimCirMan.hpp"
#include "synthesisEngines.hpp"
#include "selectionStrategies.hpp"

ABC_NAMESPACE_HEADER_START
  Gia_Man_t * Gia_ManDeepSyn( Gia_Man_t * pGia, int nIters, int nNoImpr, int TimeOut, int nAnds, int Seed, int fUseTwo, int fChoices, int fVerbose );
  int Abc_NtkMfs( Abc_Ntk_t * pNtk, Mfs_Par_t * pPars );
ABC_NAMESPACE_HEADER_END

ABC_NAMESPACE_IMPL_START

eSLIM::eSLIMConfig getCfg(const eSLIM_ParamStruct* params) {
  eSLIM::eSLIMConfig config;
  config.apply_strash = params->apply_strash;
  config.fill_subcircuits = params->fill_subcircuits;
  config.fix_seed = params->fix_seed;
  config.trial_limit_active = params->trial_limit_active;
  config.timeout = params->timeout;
  config.iterations = params->iterations;
  config.subcircuit_max_size = params->subcircuit_max_size;
  config.additional_gates = params->additional_gates;
  config.strash_intervall = params->strash_intervall;
  config.nselection_trials = params->nselection_trials;
  config.seed = params->seed;
  config.verbosity_level = params->verbosity_level;
  config.expansion_probability = params->expansion_probability;
  config.approximate_relation = params->approximate_relation;
  config.relation_tfo_bound = params->relation_tfo_bound;
  config.generate_relation_with_tfi_limit = params->generate_relation_with_tfi_limit;
  config.relation_tfi_bound = params->relation_tfi_bound;
  config.nwindows = params->nWindows;
  config.window_size = params->window_size;

  config.aig = params->aig;
  config.gate_size = params->gate_size;

  if (params->use_taboo_list) {
    config.apply_strash = false;
  }

  return config;
}

void seteSLIMParams(eSLIM_ParamStruct* params) {

  params->fill_subcircuits = 0;
  params->apply_strash = 1;
  params->fix_seed = 0;
  params->trial_limit_active = 1;
  params->apply_inprocessing = 1;
  params->synthesis_approach = 0;
  params->seed = 0;
  params->verbosity_level = 1;
  params->use_taboo_list = 0;
  params->forward_search = 0;

  params->nWindows = 0;
  params->window_size = 500;

  params->criticial_path_selection_bias = 0;

  params->timeout = 1800;
  params->iterations = 0;
  params->subcircuit_max_size = 6;
  params->additional_gates = 0;
  params->strash_intervall = 100;
  params->nselection_trials = 100;
  params->nruns = 1;

  params->expansion_probability = 0.5;    
  params->approximate_relation = 0;
  params->relation_tfo_bound = 0;   
  params->generate_relation_with_tfi_limit = 0;
  params->relation_tfi_bound = 0;
  
  params->aig = 1;
  params->gate_size = 2;
}

namespace eSLIM {

  class DeepsynInprocessor {
    public:
      DeepsynInprocessor(eSLIMConfig& config);
      void setTimeout(int timeout) {this->timeout = timeout;}
      void runInprocessing(eSLIMCirMan& es_man);
    private:
      eSLIMConfig& config;
      int timeout;
  };

  class MfsInprocessor {
    public:
      MfsInprocessor(eSLIMConfig& config) {}
      void runInprocessing(eSLIMCirMan& es_man);
  };

  class EmptyInprocessor {
    public:
      EmptyInprocessor(eSLIMConfig& config) {}
      void runInprocessing(eSLIMCirMan& es_man) {}
  };

  template <bool IncreaseSize>
  class DelayInprocessor {
    public:
      DelayInprocessor(eSLIMConfig& config);
      void setTimeout(int timeout) {this->timeout = timeout;}
      void runInprocessing(eSLIMCirMan& es_man);
    private:
      eSLIMConfig& config;
      int timeout;
  };

  class Resyn2Inprocessor {
    public:
      Resyn2Inprocessor(eSLIMConfig& config);
      void runInprocessing(eSLIMCirMan& es_man);
    private:
      // eSLIMConfig& config;
  };


  template <typename Approach, typename Inprocessor>
  class eSLIMRun {

    public:
      static Gia_Man_t* runeSLIM(Gia_Man_t* cir, const eSLIM_ParamStruct* params);
      static Abc_Ntk_t * runeSLIM(Abc_Ntk_t * cir, const eSLIM_ParamStruct* params);
      static Abc_Ntk_t * runeSLIMCells(Abc_Ntk_t * cir, const eSLIM_ParamStruct* params);

    private:
      eSLIMCirMan es_man;
      eSLIMConfig config;
      eSLIMLog log;
      Inprocessor inprocessor;

      int initial_area;
      int initial_delay;

      int area_change_eslim = 0;
      int area_change_inprocessing = 0;

      int delay_change_eslim = 0;
      int delay_change_inprocessing = 0;

      int nruns = 0;

      eSLIMRun(Gia_Man_t* cir, const eSLIM_ParamStruct* params);
      eSLIMRun(Abc_Ntk_t* cir, const eSLIM_ParamStruct* params, bool simplify);

      void setUpInprocessor();
      void runInprocessing();

      void printSettings();
      void printLog();

      void run();
  };


  template <typename Approach, typename Inprocessor>
  eSLIMRun<Approach, Inprocessor>::eSLIMRun(Abc_Ntk_t* cir, const eSLIM_ParamStruct* params, bool simplify_circuit)
                            : es_man(cir, simplify_circuit), config(getCfg(params)), log(params->subcircuit_max_size), inprocessor(config)  {
    nruns = params->nruns;
    config.aig = false;
    config.apply_strash = false;
  }

  template <typename Approach, typename Inprocessor>
  eSLIMRun<Approach, Inprocessor>::eSLIMRun(Gia_Man_t* cir, const eSLIM_ParamStruct* params)
                            : es_man(cir), config(getCfg(params)), log(params->subcircuit_max_size), inprocessor(config)  {
    nruns = params->nruns;
  }

  template <typename Approach, typename Inprocessor>
  Gia_Man_t* eSLIMRun<Approach, Inprocessor>::runeSLIM(Gia_Man_t* cir, const eSLIM_ParamStruct* params) {

    eSLIMRun<Approach, Inprocessor> eslim_run(cir, params);
    // It seems like the variables are freed by abc during preprocessing. Thus,we copy them
    char* name = Abc_UtilStrsav( cir->pName );
    char* spec = Abc_UtilStrsav( cir->pSpec );
    eslim_run.run();
    Gia_Man_t* gia_res = eslim_run.es_man.eSLIMCirManToGia();
    gia_res->pName = name;
    gia_res->pSpec = spec;
    return gia_res;
  }

  template <typename Approach, typename Inprocessor>
  Abc_Ntk_t* eSLIMRun<Approach, Inprocessor>::runeSLIM(Abc_Ntk_t* cir, const eSLIM_ParamStruct* params) {
    bool simplify_circuit = true;
    eSLIMRun<Approach, Inprocessor> eslim_run(cir, params, simplify_circuit);
    eslim_run.config.aig = false;
    eslim_run.config.apply_strash = false;
    eslim_run.run();
    Abc_Ntk_t* ntk_res = eslim_run.es_man.eSLIMCirManToNtk();
    ntk_res->pName = Extra_UtilStrsav(cir->pName);
    ntk_res->pSpec = Extra_UtilStrsav(cir->pSpec);
    return ntk_res;
  }

  template <typename Approach, typename Inprocessor>
  Abc_Ntk_t * eSLIMRun<Approach, Inprocessor>::runeSLIMCells(Abc_Ntk_t * cir, const eSLIM_ParamStruct* params) {
    bool simplify_circuit = false;
    eSLIMRun<Approach, Inprocessor> eslim_run(cir, params, simplify_circuit);
    eslim_run.config.aig = false;
    eslim_run.config.apply_strash = false;
    eslim_run.run();
    Abc_Ntk_t* ntk_res = eslim_run.es_man.eSLIMCirManToMappedNtk();
    if (!ntk_res) {
      return ntk_res;
    }
    ntk_res->pName = Extra_UtilStrsav(cir->pName);
    ntk_res->pSpec = Extra_UtilStrsav(cir->pSpec);
    return ntk_res;
  }

  template <typename Approach, typename Inprocessor>
  void eSLIMRun<Approach, Inprocessor>::run() {
    if constexpr ( std::is_same_v<DeepsynInprocessor, Inprocessor> || std::is_same_v<DelayInprocessor<true>, Inprocessor> || std::is_same_v<DelayInprocessor<false>, Inprocessor> ) {
      int time_out_inprocessing;
      if constexpr ( std::is_same_v<DeepsynInprocessor, Inprocessor> ) {
        time_out_inprocessing = config.timeout * 0.1;
      } else {
        time_out_inprocessing = config.timeout * 0.02;
      }
      config.timeout -= time_out_inprocessing;
      inprocessor.setTimeout(time_out_inprocessing);
    }

    initial_area = es_man.getNofGates();
    initial_delay = es_man.getDepth();
    printSettings();
    for (int i = 0; i < nruns; i++) {

      int size_iteration_start = es_man.getNofGates();
      int delay_iteration_start = es_man.getDepth();
      if (config.verbosity_level > 0) {
        std::cout << "Start eslim run " << i + 1 << "\n";
      }
      Approach::applyeSLIM(es_man, config, log);
      int size_eslim = es_man.getNofGates();
      int delay_eslim = es_man.getDepth();
      area_change_eslim += size_eslim - size_iteration_start;
      delay_change_eslim += delay_eslim - delay_iteration_start;
      if (config.verbosity_level > 0) {
        std::cout << "eslim -- size: " << size_eslim << " delay: " << delay_eslim << "\n";
      }
      runInprocessing();
      if constexpr ( !std::is_same_v<EmptyInprocessor, Inprocessor> ) {
        int size_inprocessing = es_man.getNofGates();
        int delay_inprocessing = es_man.getDepth();
        area_change_inprocessing += size_inprocessing - size_eslim;
        delay_change_inprocessing += delay_inprocessing - delay_eslim;
        if (config.verbosity_level > 0) {
          std::cout << "inprocessing -- size: " << size_inprocessing << " delay: " << delay_inprocessing << "\n";
        }
      }
      if (config.fix_seed) {
        config.seed++;
      }
    }
    printLog();
  }

  template <typename Approach, typename Inprocessor>
  void eSLIMRun<Approach, Inprocessor>::printSettings() {
    std::cout << "Apply eSLIM (timeout: " << config.timeout << ") ";
    std::cout << nruns << " times.\n";
    if constexpr ( !std::is_same_v<EmptyInprocessor, Inprocessor> ) {
      std::cout << "Apply inprocessing\n";
    }
    if (config.iterations > 0) {
      std::cout << "Stop eSLIM runs after " << config.iterations << " iterations.\n";
    }
    std::cout << "Consider subcircuits with up to " << config.subcircuit_max_size << " gates.\n";
    printf("When expanding subcircuits, select gates with a probability of %.2f %%.\n", 100 * config.expansion_probability);
  }

  template <typename Approach, typename Inprocessor>
  void eSLIMRun<Approach, Inprocessor>::printLog() {
    if (config.verbosity_level > 0) {
      int size = es_man.getNofGates();
      int delay = es_man.getDepth();
      int total_area_change = area_change_eslim + area_change_inprocessing;
      int total_delay_change = delay_change_eslim + delay_change_inprocessing;
      std::cout << "Final size: " << size << " change: " << total_area_change << "\n";
      std::cout << "Final depth: " << delay << " change: " << total_delay_change << "\n";


      if constexpr ( !std::is_same_v<EmptyInprocessor, Inprocessor> ) {

        if (area_change_eslim <= 0) {
          std::cout << "#Gates reduced by eSLIM: " << -area_change_eslim << "\n";
        } else {
          std::cout << "#Gates introduced by eSLIM: " << area_change_eslim << "\n";
        }
        if (area_change_inprocessing <= 0) {
          std::cout << "#Gates reduced by inprocessing: " << -area_change_inprocessing << "\n";
        } else {
          std::cout << "#Gates introduced by inprocessing: " << area_change_inprocessing << "\n";
        }
        if (delay_change_eslim <= 0) {
          std::cout << "Delay reduction eSLIM: " << -delay_change_eslim << "\n";
        } else {
          std::cout << "Delay increase eSLIM: " << delay_change_eslim << "\n";
        }
        if (delay_change_inprocessing <= 0) {
          std::cout << "Delay reduction inprocessing: " << -delay_change_inprocessing << "\n";
        } else {
          std::cout << "Delay increase inprocessing: " << delay_change_inprocessing << "\n";
        }
      }
    }
    if (config.verbosity_level > 1) {
      std::cout << "Total #iterations: " << log.iteration_count << "\n";
      std::cout << "Total relation generation time (s): " << log.relation_generation_time << "\n";
      std::cout << "Total synthesis time (s): " << log.synthesis_time << "\n";
      std::cout << "#Iterations with forbidden pairs: " << log.subcircuits_with_forbidden_pairs << "\n";
      printf("Relation generation: #SAT-calls: %lu, Total SAT-time: %.2f sec, Max SAT-time: %.4f sec, Avg. SAT-time: %.4f sec\n",  
            log.nof_sat_calls_relation_generation, log.cummulative_sat_runtime_relation_generation, log.max_sat_runtime_relation_generation, 
            log.cummulative_sat_runtime_relation_generation/log.nof_sat_calls_relation_generation);
      printf("Synthesis: #SAT-calls: %lu, Total SAT-time: %.2f sec, Max SAT-time: %.4f sec, Avg. SAT-time: %.4f sec\n",  
            log.nof_sat_calls_synthesis, log.cummulative_sat_runtime_synthesis / 1000, log.max_sat_runtime_synthesis / 1000,
            log.cummulative_sat_runtime_synthesis / (log.nof_sat_calls_synthesis*1000));
    }
    if (config.verbosity_level > 2) {
      for (int i = 2; i < log.nof_analyzed_circuits_per_size.size(); i++) {
        int replaced = log.nof_replaced_circuits_per_size.size() > i ? log.nof_replaced_circuits_per_size[i] : 0;
        int reduced = log.nof_reduced_circuits_per_size.size() > i ? log.nof_reduced_circuits_per_size[i] : 0;
        std::cout << "#gates: " << i << " - #analysed: " << log.nof_analyzed_circuits_per_size[i] << " - #replaced: " << replaced << " - #reduced: " << reduced;
        std::cout << " - avg. relation time: ";
        if (log.nof_relation_generations_per_size[i] == 0) {
          std::cout << "-";
        } else {
          printf("%.2f sec",  static_cast<double>(log.cummulative_relation_generation_times_per_size[i]) / (log.nof_relation_generations_per_size[i]));
        }
        std::cout << "\n";
      }
      for (int i = 0; i < log.nof_sat_calls_per_size.size(); i++) {
        std::cout << "#gates: " << i << " - avg. sat time: ";
        if (log.nof_sat_calls_per_size[i] == 0) {
          std::cout << "-";
        } else {
          printf("%.2f sec",  static_cast<double>(log.cummulative_sat_runtimes_per_size[i]) / (1000 * log.nof_sat_calls_per_size[i]));
        }
        std::cout << " / avg. unsat time: ";
        if (log.nof_unsat_calls_per_size[i] == 0) {
          std::cout << "-";
        } else {
          printf("%.2f sec",  static_cast<double>(log.cummulative_unsat_runtimes_per_size[i]) / (1000 * log.nof_unsat_calls_per_size[i]));
        }
        std::cout << "\n";
      }
    }
  }

  template <typename Approach, typename Inprocessor>
  void eSLIMRun<Approach, Inprocessor>::runInprocessing() {
    inprocessor.runInprocessing(es_man);
  }

  template <typename Approach, typename Inprocessor>
  void setUpInprocessor();

  DeepsynInprocessor::DeepsynInprocessor(eSLIMConfig& config)
                    : config(config) {
  }

  void DeepsynInprocessor::runInprocessing(eSLIMCirMan& es_man) {
    Gia_Man_t* pGia = es_man.eSLIMCirManToGia();
    Gia_Man_t* tmp = Gia_ManDeepSyn( pGia, 1, ABC_INFINITY, timeout, 0, config.seed , 0, 0, 0);
    if ( Gia_ManAndNum(pGia) > Gia_ManAndNum(tmp) ) {
      es_man = eSLIMCirMan(tmp);
    }
    Gia_ManStop( tmp );
    Gia_ManStop( pGia );
  }

  void MfsInprocessor::runInprocessing(eSLIMCirMan& es_man) {
    Abc_Ntk_t* pNtk = es_man.eSLIMCirManToNtk();
    int nnodes = Abc_NtkNodeNum(pNtk);
    Mfs_Par_t Pars, * pPars = &Pars;
    Abc_NtkMfsParsDefault( pPars );
    if ( Abc_NtkMfs( pNtk, pPars ) ) {
      Abc_NtkToSop( pNtk, -1, ABC_INFINITY );
      if (Abc_NtkNodeNum(pNtk) <= nnodes) {
        es_man = eSLIMCirMan(pNtk, true);
      }
    }
    Abc_NtkDelete(pNtk);
  }

  template <bool IncreaseSize>
  DelayInprocessor<IncreaseSize>::DelayInprocessor(eSLIMConfig& config)
                                : config(config) {
  }

  template <bool IncreaseSize>
  void DelayInprocessor<IncreaseSize>::runInprocessing(eSLIMCirMan& es_man) {
    abctime clkStart    = Abc_Clock();
    abctime nTimeToStop = clkStart + timeout * CLOCKS_PER_SEC;
    Gia_Man_t* pGia = es_man.eSLIMCirManToGia();
    int nand = Gia_ManAndNum(pGia);
    int nlevel = Gia_ManLevelNum(pGia);
    char * resyn2 = "balance; rewrite; rewrite -z; balance; rewrite -z; balance";
    char Command[2000];
    sprintf( Command, "strash; if -g -K 6; %s; if -K 6", resyn2 );
    bool reset_batch_mode = false ;
    if ( !Abc_FrameIsBatchMode() ) {
      reset_batch_mode = true;
      Abc_FrameSetBatchMode( 1 );
    }
    Abc_FrameUpdateGia( Abc_FrameGetGlobalFrame(), pGia );
    if ( Cmd_CommandExecute(Abc_FrameGetGlobalFrame(), "&put") ) {
      return;
    }
    while (Abc_Clock() <= nTimeToStop) {
      if ( Cmd_CommandExecute(Abc_FrameGetGlobalFrame(), Command) ) {
        Abc_Print( 1, "Something did not work out with the command \"%s\".\n", Command );
        return;
      }
    }
    if ( Cmd_CommandExecute(Abc_FrameGetGlobalFrame(), "&get") ) {
      return;
    }
    if (reset_batch_mode) {
      Abc_FrameSetBatchMode( 0 );
    }
    Gia_Man_t* tmp = Abc_FrameReadGia(Abc_FrameGetGlobalFrame());
    if constexpr (!IncreaseSize) {
      if (Gia_ManAndNum(tmp) > nand) {
        return;
      }
    }
    if (Gia_ManLevelNum(tmp) <= nlevel) {
      es_man = eSLIMCirMan(tmp);
    }
  }

  Resyn2Inprocessor::Resyn2Inprocessor(eSLIMConfig& config)
                    // : config(config) 
                    {
  }

  void Resyn2Inprocessor::runInprocessing(eSLIMCirMan& es_man) {
    Gia_Man_t* pGia = es_man.eSLIMCirManToGia();
    int nand = Gia_ManAndNum(pGia);
    int nlevel = Gia_ManLevelNum(pGia);
    char * resyn2 = "balance; rewrite; rewrite -z; balance; rewrite -z; balance";
    bool reset_batch_mode = false ;
    if ( !Abc_FrameIsBatchMode() ) {
      reset_batch_mode = true;
      Abc_FrameSetBatchMode( 1 );
    }
    Abc_FrameUpdateGia( Abc_FrameGetGlobalFrame(), pGia );
    if ( Cmd_CommandExecute(Abc_FrameGetGlobalFrame(), "&put") ) {
      return;
    }
    if ( Cmd_CommandExecute(Abc_FrameGetGlobalFrame(), resyn2) ) {
      Abc_Print( 1, "Something did not work out with the command \"%s\".\n", resyn2 );
      return;
    }
    if ( Cmd_CommandExecute(Abc_FrameGetGlobalFrame(), "&get") ) {
      return;
    }
    if (reset_batch_mode) {
      Abc_FrameSetBatchMode( 0 );
    }
    Gia_Man_t* tmp = Abc_FrameReadGia(Abc_FrameGetGlobalFrame());
    if (Gia_ManAndNum(tmp) <= nand && Gia_ManLevelNum(tmp) <= nlevel) {
      es_man = eSLIMCirMan(tmp);
    }
  }



  template <typename Circuitrepresentation, typename Inprocessor, typename Minimizer, typename Approach>
  Circuitrepresentation* runeSLIM(Circuitrepresentation * cir, const eSLIM_ParamStruct* params) {
    return eSLIMRun<Approach, Inprocessor>::runeSLIM(cir, params); 
  }

  template <typename Circuitrepresentation, typename Inprocessor, typename Minimizer, typename Validator, typename SearchDirection, typename BiasSelector, typename RootSelector>
  Circuitrepresentation* toggleWindowing(Circuitrepresentation * cir, const eSLIM_ParamStruct* params) {
    if (params->nWindows > 0) {
      return runeSLIM<Circuitrepresentation, Inprocessor, Minimizer, WindowMan<Minimizer, RandomizedBFSSelection<Validator, RootSelector, SearchDirection, BiasSelector>>>(cir, params);
    } else {
      return runeSLIM<Circuitrepresentation, Inprocessor, Minimizer, eSLIM_Man<Minimizer, RandomizedBFSSelection<Validator, RootSelector, SearchDirection, BiasSelector>>>(cir, params);
    }
  }


  template <typename Circuitrepresentation, typename Inprocessor, typename Minimizer, typename Validator, typename SearchDirection, typename BiasSelector>
  Circuitrepresentation* setTabooApproach(Circuitrepresentation * cir, const eSLIM_ParamStruct* params) {
    if (params->use_taboo_list) {
      return toggleWindowing<Circuitrepresentation, Inprocessor, Minimizer, Validator, SearchDirection, BiasSelector, TabooRootSelector>(cir, params);
    } else {
      return toggleWindowing<Circuitrepresentation, Inprocessor, Minimizer, Validator, SearchDirection, BiasSelector, BaseRootSelector>(cir, params);
    }
  }

  template <typename Circuitrepresentation, typename Inprocessor, typename Minimizer, typename Validator, typename SearchDirection>
  Circuitrepresentation* setBiasApproach(Circuitrepresentation * cir, const eSLIM_ParamStruct* params) {
    if (params->criticial_path_selection_bias) {
      return toggleWindowing<Circuitrepresentation, Inprocessor, Minimizer, Validator, SearchDirection, CriticalPathBiasSelector, CriticalPathSelector>(cir, params);
    } else {
      return setTabooApproach<Circuitrepresentation, Inprocessor, Minimizer, Validator, SearchDirection, NoBiasSelector>(cir, params);
    }
  }



  template <typename Circuitrepresentation, typename Inprocessor, typename Minimizer, typename Validator>
  Circuitrepresentation* setSearchDirection(Circuitrepresentation * cir, const eSLIM_ParamStruct* params) {
    if (params->forward_search) {
      return setBiasApproach<Circuitrepresentation, Inprocessor, Minimizer, Validator, ForwardSearch>(cir, params);
    } else {
      return setBiasApproach<Circuitrepresentation, Inprocessor, Minimizer, Validator, BackwardSearch>(cir, params);
    }
  }

  template <typename Circuitrepresentation>  
  Circuitrepresentation* setMinimizer(Circuitrepresentation * cir, const eSLIM_ParamStruct* params) {
    switch (params->synthesis_approach) {
      case 0:
        if (params->apply_inprocessing) {
          if constexpr ( std::is_same_v<Gia_Man_t, Circuitrepresentation> ) {
            return setSearchDirection<Circuitrepresentation, DeepsynInprocessor, AreaMinimizer, SubcircuitValidator>(cir, params);
          } else {
            return setSearchDirection<Circuitrepresentation, MfsInprocessor, AreaMinimizer, SubcircuitValidator>(cir, params);
          }
        }
        return setSearchDirection<Circuitrepresentation, EmptyInprocessor, AreaMinimizer, SubcircuitValidator>(cir, params);
      case 1:
        if constexpr ( std::is_same_v<Gia_Man_t, Circuitrepresentation> ) {
          if (params->apply_inprocessing) {
            return setSearchDirection<Circuitrepresentation, Resyn2Inprocessor, AreaDelayConstraintMinimizer, SubcircuitNoFBValidator>(cir, params);
          } 
        }
        return setSearchDirection<Circuitrepresentation, EmptyInprocessor, AreaDelayConstraintMinimizer, SubcircuitNoFBValidator>(cir, params);
      case 2:
        if constexpr ( std::is_same_v<Gia_Man_t, Circuitrepresentation> ) {
          if (params->apply_inprocessing) {
            return setSearchDirection<Circuitrepresentation, DelayInprocessor<false>, AreaDelayMinimizer, SubcircuitNoFBValidator>(cir, params);
          } 
        }
        return setSearchDirection<Circuitrepresentation, EmptyInprocessor, AreaDelayMinimizer, SubcircuitNoFBValidator>(cir, params);
      case 3:
        if constexpr ( std::is_same_v<Gia_Man_t, Circuitrepresentation> ) {
          if (params->apply_inprocessing) {
            return setSearchDirection<Circuitrepresentation, DelayInprocessor<false>, DelayMinimizer, SubcircuitNoFBValidator>(cir, params);
          } 
        }
        return setSearchDirection<Circuitrepresentation, EmptyInprocessor, DelayMinimizer, SubcircuitNoFBValidator>(cir, params);
      default:
        assert(false);
        return NULL;
    }
  }
}

template <typename Circuitrepresentation> 
Circuitrepresentation* runeSLIM(Circuitrepresentation * cir, const eSLIM_ParamStruct* params) {
  // Windowing does currently not support Deleyconstraints/Delayoptimisation
  if (params->nWindows && params->synthesis_approach != 0) {
    return NULL;
  }
  return eSLIM::setMinimizer(cir, params);
}


Gia_Man_t* applyeSLIM(Gia_Man_t * pGia, const eSLIM_ParamStruct* params) {
  return runeSLIM(pGia, params);
}

Abc_Ntk_t* applyelSLIM(Abc_Ntk_t * ntk, const eSLIM_ParamStruct* params) {
  return runeSLIM(ntk, params);
}

Abc_Ntk_t* applyetSLIM(Abc_Ntk_t * ntk, const eSLIM_ParamStruct* params) {
  return runeSLIM(ntk, params);
}






ABC_NAMESPACE_IMPL_END
