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

 Copyright (C) 1994-2000 the Omega Project Team
 Copyright (C) 2005-2011 Chun Chen
 All Rights Reserved.

 Purpose:
 CG node classes, used to build AST tree from polyhedra scanning.

 Notes:
 Parameter "restriction" is always tighter than "known" since CG_split
 node does not correspond to any code for enforcement. This property is
 destroyed after hoistGuard since "restriction" is not used anymore.
 CG node's children are guaranteed not to be NULL, either NULL child is
 removed from the children or the parent node itself becomes NULL.

 History:
 04/20/96 printRepr added by D people. Lei Zhou
 10/24/06 hoistGuard added by chun
 08/03/10 collect CG classes into one place, by Chun Chen
 08/04/10 track dynamically substituted variables in printRepr, by chun
 04/02/11 rewrite the CG node classes, by chun
*****************************************************************************/

#include <typeinfo>
#include <assert.h>
#include <omega.h>
#include <code_gen/codegen.h>
#include <code_gen/CG.h>
#include <code_gen/CG_outputBuilder.h>
#include <code_gen/CG_stringBuilder.h>
#include <code_gen/CG_utils.h>
#include <code_gen/codegen_error.h>
#include <stack>
#include <string.h>

namespace omega {
  
  extern std::vector<std::vector<int> > smtNonSplitLevels;
  extern std::vector<std::vector<std::string> > loopIdxNames; //per stmt
  extern std::vector<std::pair<int, std::string> > syncs;
  
  extern int checkLoopLevel;
  extern int stmtForLoopCheck;
  extern int upperBoundForLevel;
  extern int lowerBoundForLevel;
  extern bool fillInBounds;
  
  //-----------------------------------------------------------------------------
  // Class: CG_result
  //-----------------------------------------------------------------------------
  
  CG_outputRepr *CG_result::printRepr(CG_outputBuilder *ocg,
                                      const std::vector<CG_outputRepr *> &stmts,
                                      std::vector<std::map<std::string, std::vector<CG_outputRepr *> > > uninterpreted_symbols, 
                                      bool printString) const {
    fprintf(stderr, "\nCG_result::printRepr(ocg, stmts) \n"); 
    //Anand: making a tweak to allocate twice the original number of dynamically allocated variables
    //for use with Uninterpreted function symbols
    
    //Anand: adding support for Replacing substituted variables within
    //Uninterpreted function symbols or global variables with arity > 0 here
    //--begin
    
    // check for an error that happened once
    int num_unin = uninterpreted_symbols.size();
    int num_active =  active_.size();
    if (num_unin < num_active) { 
      fprintf(stderr, "CG.cc CG_result::printRepr(), not enough uninterpreted symbols (%d) for active statements (5d)\n",  num_unin,  num_active); 
      exit(-1); 
    }

    std::vector<std::pair<CG_outputRepr *, int> > aotf = std::vector<
      std::pair<CG_outputRepr *, int> >(2 * num_level(),
                                        std::make_pair(static_cast<CG_outputRepr *>(NULL), 0));

#define DYINGHERE 
#ifdef DYINGHERE
    int num_levels = num_level();
    
    for (int s = 0; s < active_.size(); s++) {
      fprintf(stderr, "\ns %d\n", s); 
      std::vector<std::string> loop_vars;
      if (active_.get(s)) {
        
        Relation mapping = Inverse(
          copy((codegen_->xforms_[codegen_->remap_[s]])));
        
        mapping.simplify();
        mapping.setup_names();
        
        for (int i = 1; i <= mapping.n_out(); i++)
          loop_vars.push_back(mapping.output_var(i)->name());
        
        std::vector<CG_outputRepr *> subs_;
        for (int i = 1; i <= mapping.n_out(); i++) {
          Relation mapping1(mapping.n_out(), 1);
          F_And *f_root = mapping1.add_and();
          EQ_Handle h = f_root->add_EQ();
          h.update_coef(mapping1.output_var(1), 1);
          h.update_coef(mapping1.input_var(i), -1);
          Relation r = Composition(mapping1, copy(mapping));
          r.simplify();
          
          //Relation r = copy(mapping);
          
          Variable_ID v = r.output_var(1);
          loop_vars.push_back(mapping.output_var(i)->name());
          
          std::pair<EQ_Handle, int> result = find_simplest_assignment(r,
                                                  v, aotf);
          
          std::string hand = result.first.print_to_string();
          //fprintf(stderr, "result: %s, %d\n", hand.c_str(), result.second); 
          if (result.second < INT_MAX) {
            
            CG_outputRepr *subs = output_substitution_repr(ocg,
                                                           result.first, 
                                                           v, 
                                                           true, 
                                                           r, 
                                                           aotf,
                                                           uninterpreted_symbols[s]);
            
            subs_.push_back(subs->clone());
            
            aotf[num_levels + i - 1] = std::make_pair(subs, 999);
            
          } else {
            CG_outputRepr* repr = NULL;
            
            aotf[num_levels + i - 1] = std::make_pair(repr, 999);
          }
        }
        if(!printString)
          for (std::map<std::string, std::vector<CG_outputRepr *> >::iterator it =
                 uninterpreted_symbols[s].begin();
               it != uninterpreted_symbols[s].end(); it++) {
            std::vector<CG_outputRepr *> reprs_ = it->second;
            std::vector<CG_outputRepr *> reprs_2;
            
            for (int k = 0; k < reprs_.size(); k++) {
              std::vector<CG_outputRepr *> subs2;
              for (int l = 0; l < subs_.size(); l++) {
                
                subs2.push_back(subs_[l]->clone());
              }
              CG_outputRepr * temp =
                ocg->CreateSubstitutedStmt(0, reprs_[k]->clone(),
                                           loop_vars, subs2, false);
              
              if (temp != NULL)
                reprs_2.push_back(temp);
              //    reprs_2.push_back(subs_[k]->clone());
              
            }
            if(reprs_2.size() > 0)
              it->second = reprs_2;
          }
        
        //break;
      }
    }
    
//--end
    
#endif

    fprintf(stderr, "\n\n\n\nprintRepr recursing ??? return printRepr( ... )\n"); 
    return printRepr(1, ocg, stmts, aotf, uninterpreted_symbols, printString);
  }
  


  std::string CG_result::printString(
    std::vector<std::map<std::string, std::vector<CG_outputRepr *> > > uninterpreted_symbols) const {

    fprintf(stderr, "CG.cc line 164, CG_result::printString()\n"); 
    CG_stringBuilder ocg;
    std::vector<CG_outputRepr *> stmts(codegen_->xforms_.size());

    fprintf(stderr, "stmts.size() %d\n", stmts.size()); 
    for (int i = 0; i < stmts.size(); i++)
      stmts[i] = new CG_stringRepr("s" + to_string(i));

    CG_stringRepr *repr = static_cast<CG_stringRepr *>(printRepr(&ocg, 
                                                                 stmts,
                                                                 uninterpreted_symbols, 
                                                                 true));

    for (int i = 0; i < stmts.size(); i++)
      delete stmts[i];
    
    if (repr != NULL) {
      std::string s = repr->GetString();
      //fprintf(stderr, "\nCG.cc L197 repr->GetString() = '%s'\n\n\n", s.c_str()); 
      delete repr;
      return s;
    } else
      return std::string();
  }
  
  int CG_result::num_level() const {
    return codegen_->num_level();
  }
  
  //-----------------------------------------------------------------------------
  // Class: CG_split
  //-----------------------------------------------------------------------------
  
  CG_result *CG_split::recompute(const BoolSet<> &parent_active,
                                 const Relation &known, const Relation &restriction) {
    active_ &= parent_active;
    if (active_.empty()) {
      delete this;
      return NULL;
    }
    
    
    int i = 0;
    while (i < restrictions_.size()) {
      Relation new_restriction = Intersection(copy(restrictions_[i]),
                                              copy(restriction));
      
      new_restriction.simplify(2, 4);
      //new_restriction.simplify();
      clauses_[i] = clauses_[i]->recompute(active_, copy(known),
                                           new_restriction);
      if (clauses_[i] == NULL) {
        restrictions_.erase(restrictions_.begin() + i);
        clauses_.erase(clauses_.begin() + i);
      } else
        i++;
    }
    
    
    if (restrictions_.size() == 0) {
      delete this;
      return NULL;
    } else
      return this;
  }
  
  int CG_split::populateDepth() {
    int max_depth = 0;
    for (int i = 0; i < clauses_.size(); i++) {
      int t = clauses_[i]->populateDepth();
      if (t > max_depth)
        max_depth = t;
    }
    return max_depth;
  }
  
  std::pair<CG_result *, Relation> CG_split::liftOverhead(int depth,
                                                          bool propagate_up) {
    for (int i = 0; i < clauses_.size();) {
      std::pair<CG_result *, Relation> result = clauses_[i]->liftOverhead(
                                                                          depth, propagate_up);
      if (result.first == NULL)
        clauses_.erase(clauses_.begin() + i);
      else {
        clauses_[i] = result.first;
        if (!result.second.is_obvious_tautology())
          return std::make_pair(this, result.second);
        i++;
      }
      
    }
    
    if (clauses_.size() == 0) {
      delete this;
      return std::make_pair(static_cast<CG_result *>(NULL),
                            Relation::True(num_level()));
    } else
      return std::make_pair(this, Relation::True(num_level()));
  }
  
  Relation CG_split::hoistGuard() {
    std::vector<Relation> guards;
    for (int i = 0; i < clauses_.size(); i++)
      guards.push_back(clauses_[i]->hoistGuard());
    
    return SimpleHull(guards, true, true);
  }
  
  void CG_split::removeGuard(const Relation &guard) {
    for (int i = 0; i < clauses_.size(); i++)
      clauses_[i]->removeGuard(guard);
  }
  
  std::vector<CG_result *> CG_split::findNextLevel() const {
    std::vector<CG_result *> result;
    for (int i = 0; i < clauses_.size(); i++) {
      CG_split *splt = dynamic_cast<CG_split *>(clauses_[i]);
      if (splt != NULL) {
        std::vector<CG_result *> t = splt->findNextLevel();
        result.insert(result.end(), t.begin(), t.end());
      } else
        result.push_back(clauses_[i]);
    }
    
    return result;
  }
  



  CG_outputRepr *CG_split::printRepr(int indent, 
                                     CG_outputBuilder *ocg,
                                     const std::vector<CG_outputRepr *> &stmts,
                                     const std::vector<std::pair<CG_outputRepr *, int> > &assigned_on_the_fly,
                                     std::vector<std::map<std::string, std::vector<CG_outputRepr *> > > unin, 
                                     bool printString) const {
    
    fprintf(stderr, "CG_split::printRepr()\n"); 
    int numfly =  assigned_on_the_fly.size();
    //fprintf(stderr, "assigned on the fly  %d\n", numfly );
    //for (int i=0; i<numfly; i++) { 
    //  fprintf(stderr, "i %d\n", i); 
    //  std::pair<CG_outputRepr *, int>p = assigned_on_the_fly[i];
    //  CG_outputRepr *tr = NULL;
    //  if (p.first != NULL) tr = p.first->clone();
    //  int val = p.second;
    //  fprintf(stderr, "0x%x   %d\n", tr, val);
    //} 
    
    CG_outputRepr *stmtList = NULL;
    std::vector<CG_result *> next_level = findNextLevel();
    
    std::vector<CG_loop *> cur_loops;
    for (int i = 0; i < next_level.size(); i++) {
      CG_loop *lp = dynamic_cast<CG_loop *>(next_level[i]);
      if (lp != NULL) {
        cur_loops.push_back(lp);
      } else {
        stmtList = ocg->StmtListAppend(stmtList,
                                       loop_print_repr(active_, 
                                                       cur_loops, 
                                                       0, 
                                                       cur_loops.size(),
                                                       Relation::True(num_level()), 
                                                       NULL, 
                                                       indent, 
                                                       codegen_->remap_, 
                                                       codegen_->xforms_, 
                                                       ocg,
                                                       stmts, 
                                                       assigned_on_the_fly,
                                                       unin));
        stmtList = ocg->StmtListAppend(stmtList,
                                       next_level[i]->printRepr(indent, 
                                                                ocg, 
                                                                stmts,
                                                                assigned_on_the_fly,
                                                                unin, 
                                                                printString));
        cur_loops.clear();
      }
    }
    
    stmtList = ocg->StmtListAppend(stmtList,
                                   loop_print_repr(active_,
                                                   cur_loops, 
                                                   0, 
                                                   cur_loops.size(),
                                                   Relation::True(num_level()), 
                                                   NULL, 
                                                   indent, 
                                                   codegen_->remap_,
                                                   codegen_->xforms_, 
                                                   ocg, 
                                                   stmts,
                                                   assigned_on_the_fly,
                                                   unin));
    return stmtList;
  }
  
  CG_result *CG_split::clone() const {
    //fprintf(stderr, "CG_split::clone()\n"); 
    std::vector<CG_result *> clauses(clauses_.size());
    for (int i = 0; i < clauses_.size(); i++)
      clauses[i] = clauses_[i]->clone();
    return new CG_split(codegen_, active_, restrictions_, clauses);
  }
  
  void CG_split::dump(int indent) const {
    std::string prefix;
    for (int i = 0; i < indent; i++)
      prefix += "  ";
    std::cout << prefix << "SPLIT: " << active_ << std::endl;
    for (int i = 0; i < restrictions_.size(); i++) {
      std::cout << prefix << "restriction: ";
      const_cast<CG_split *>(this)->restrictions_[i].print();
      clauses_[i]->dump(indent + 1);
    }
    
  }
  
  //-----------------------------------------------------------------------------
  // Class: CG_loop
  //-----------------------------------------------------------------------------
  
  CG_result *CG_loop::recompute(const BoolSet<> &parent_active,
                                const Relation &known, const Relation &restriction) {
    known_ = copy(known);
    restriction_ = copy(restriction);
    active_ &= parent_active;
    
    std::vector<Relation> Rs;
    for (BoolSet<>::iterator i = active_.begin(); i != active_.end(); i++) {
      Relation r = Intersection(copy(restriction),
                                copy(codegen_->projected_IS_[level_ - 1][*i]));
      
      //r.simplify(2, 4);
      r.simplify();
      if (!r.is_upper_bound_satisfiable()) {
        active_.unset(*i);
        continue;
      }
      Rs.push_back(copy(r));
    }
    
    if (active_.empty()) {
      delete this;
      return NULL;
    }
    
    Relation hull = SimpleHull(Rs, true, true);
    
    //hull.simplify(2,4);
    //Anand:Variables for inspector constraint check
    bool has_insp = false;
    bool found_insp = false;
    
    //Global_Var_ID global_insp;
    //Argument_Tuple arg;
    // check if actual loop is needed
    std::pair<EQ_Handle, int> result = find_simplest_assignment(hull,
                                                                hull.set_var(level_));
    if (result.second < INT_MAX) {
      needLoop_ = false;
      
      bounds_ = Relation(hull.n_set());
      F_Exists *f_exists = bounds_.add_and()->add_exists();
      F_And *f_root = f_exists->add_and();
      std::map<Variable_ID, Variable_ID> exists_mapping;
      EQ_Handle h = f_root->add_EQ();
      for (Constr_Vars_Iter cvi(result.first); cvi; cvi++) {
        Variable_ID v = cvi.curr_var();
        switch (v->kind()) {
        case Input_Var:
          h.update_coef(bounds_.input_var(v->get_position()),
                        cvi.curr_coef());
          break;
        case Wildcard_Var: {
          Variable_ID v2 = replicate_floor_definition(hull, v, bounds_,
                                                      f_exists, f_root, exists_mapping);
          h.update_coef(v2, cvi.curr_coef());
          break;
        }
        case Global_Var: {
          Global_Var_ID g = v->get_global_var();
          Variable_ID v2;
          if (g->arity() == 0)
            v2 = bounds_.get_local(g);
          else
            v2 = bounds_.get_local(g, v->function_of());
          h.update_coef(v2, cvi.curr_coef());
          break;
        }
        default:
          assert(false);
        }
      }
      h.update_const(result.first.get_const());
      bounds_.simplify();
    }
    // loop iterates more than once, extract bounds now
    else {
      fprintf(stderr, "loop iterates more than once, extract bounds now\n"); 
      needLoop_ = true;
      
      bounds_ = Relation(hull.n_set());
      F_Exists *f_exists = bounds_.add_and()->add_exists();
      F_And *f_root = f_exists->add_and();
      std::map<Variable_ID, Variable_ID> exists_mapping;
      
      Relation b = Gist(copy(hull), copy(known), 1);
      bool has_unresolved_bound = false;
      
      std::set<Variable_ID> excluded_floor_vars;
      excluded_floor_vars.insert(b.set_var(level_));
      for (GEQ_Iterator e(b.single_conjunct()->GEQs()); e; e++)
        if ((*e).get_coef(b.set_var(level_)) != 0) {
          bool is_bound = true;
          for (Constr_Vars_Iter cvi(*e, true); cvi; cvi++) {
            std::pair<bool, GEQ_Handle> result = find_floor_definition(
                                                                       b, cvi.curr_var(), excluded_floor_vars);
            if (!result.first) {
              is_bound = false;
              has_unresolved_bound = true;
              //break;
            }
            
            if (!is_bound) {
              std::vector<std::pair<bool, GEQ_Handle> > result =
                find_floor_definition_temp(b, cvi.curr_var(),
                                           excluded_floor_vars);
              
              if (result.size() != 2)
                break;
              
              has_unresolved_bound = false;
              for (int i = 0; i < result.size(); i++) {
                
                GEQ_Handle h = f_root->add_GEQ();
                
                for (Constr_Vars_Iter cvi(result[i].second); cvi;
                     cvi++) {
                  Variable_ID v = cvi.curr_var();
                  switch (v->kind()) {
                  case Input_Var:
                    h.update_coef(
                                  bounds_.input_var(
                                                    v->get_position()),
                                  cvi.curr_coef());
                    break;
                  case Wildcard_Var: {
                    Variable_ID v2;
                    
                    v2 = replicate_floor_definition(b, v,
                                                    bounds_, f_exists, f_root,
                                                    exists_mapping);
                    
                    h.update_coef(v2, cvi.curr_coef());
                    break;
                  }
                  case Global_Var: {
                    Global_Var_ID g = v->get_global_var();
                    Variable_ID v2;
                    if (g->arity() == 0)
                      v2 = bounds_.get_local(g);
                    else
                      v2 = bounds_.get_local(g,
                                             v->function_of());
                    h.update_coef(v2, cvi.curr_coef());
                    break;
                  }
                  default:
                    assert(false);
                  }
                }
                h.update_const((result[i].second).get_const());
              }
              break;
            }
          }
          
          if (!is_bound)
            continue;
          
          GEQ_Handle h = f_root->add_GEQ();
          for (Constr_Vars_Iter cvi(*e); cvi; cvi++) {
            Variable_ID v = cvi.curr_var();
            switch (v->kind()) {
            case Input_Var:
              h.update_coef(bounds_.input_var(v->get_position()),
                            cvi.curr_coef());
              break;
            case Wildcard_Var: {
              Variable_ID v2 = replicate_floor_definition(b, v,
                                                          bounds_, f_exists, f_root, exists_mapping);
              h.update_coef(v2, cvi.curr_coef());
              break;
            }
            case Global_Var: {
              Global_Var_ID g = v->get_global_var();
              Variable_ID v2;
              if (g->arity() == 0)
                v2 = bounds_.get_local(g);
              else
                v2 = bounds_.get_local(g, v->function_of());
              h.update_coef(v2, cvi.curr_coef());
              break;
            }
            default:
              assert(false);
            }
          }
          h.update_const((*e).get_const());
        }
      
      if (has_unresolved_bound) {
        b = Approximate(b);
        b.simplify(2, 4);
        //Simplification of Hull
        hull = Approximate(hull);
        hull.simplify(2, 4);
        //end : Anand
        for (GEQ_Iterator e(b.single_conjunct()->GEQs()); e; e++)
          if ((*e).get_coef(b.set_var(level_)) != 0)
            f_root->add_GEQ(*e);
      }
      bounds_.simplify();
      hull.simplify(2,4);
      // Since current SimpleHull does not support max() upper bound or min() lower bound,
      // we have to forcefully split the loop when hull approximation does not return any bound.
      bool has_lb = false;
      bool has_ub = false;
      for (GEQ_Iterator e = bounds_.single_conjunct()->GEQs(); e; e++) {
        if ((*e).get_coef(bounds_.set_var(level_)) > 0)
          has_lb = true;
        else if ((*e).get_coef(bounds_.set_var(level_)) < 0)
          has_ub = true;
        if (has_lb && has_ub)
          break;
      }
      
      if (!has_lb) {
        for (int i = 0; i < Rs.size(); i++) {
          Relation r = Approximate(copy(Rs[i]));
          r.simplify(2, 4);
          for (GEQ_Iterator e = r.single_conjunct()->GEQs(); e; e++)
            if ((*e).get_coef(r.input_var(level_)) > 0) {
              Relation r2 = Relation::True(num_level());
              r2.and_with_GEQ(*e);
              r2.simplify();
              std::vector<Relation> restrictions(2);
              restrictions[0] = Complement(copy(r2));
              restrictions[0].simplify();
              restrictions[1] = r2;
              std::vector<CG_result *> clauses(2);
              clauses[0] = this;
              clauses[1] = this->clone();
              CG_result *cgr = new CG_split(codegen_, active_,
                                            restrictions, clauses);
              cgr = cgr->recompute(active_, copy(known),
                                   copy(restriction));
              return cgr;
            }
        }
        for (int i = 0; i < Rs.size(); i++) {
          Relation r = Approximate(copy(Rs[i]));
          r.simplify(2, 4);
          for (EQ_Iterator e = r.single_conjunct()->EQs(); e; e++)
            if ((*e).get_coef(r.input_var(level_)) != 0) {
              Relation r2 = Relation::True(num_level());
              r2.and_with_GEQ(*e);
              r2.simplify();
              std::vector<Relation> restrictions(2);
              if ((*e).get_coef(r.input_var(level_)) > 0) {
                restrictions[0] = Complement(copy(r2));
                restrictions[0].simplify();
                restrictions[1] = r2;
              } else {
                restrictions[0] = r2;
                restrictions[1] = Complement(copy(r2));
                restrictions[1].simplify();
              }
              std::vector<CG_result *> clauses(2);
              clauses[0] = this;
              clauses[1] = this->clone();
              CG_result *cgr = new CG_split(codegen_, active_,
                                            restrictions, clauses);
              cgr = cgr->recompute(active_, copy(known),
                                   copy(restriction));
              return cgr;
            }
        }
      } else if (!has_ub) {
        for (int i = 0; i < Rs.size(); i++) {
          Relation r = Approximate(copy(Rs[i]));
          r.simplify(2, 4);
          for (GEQ_Iterator e = r.single_conjunct()->GEQs(); e; e++)
            if ((*e).get_coef(r.input_var(level_)) < 0) {
              Relation r2 = Relation::True(num_level());
              r2.and_with_GEQ(*e);
              r2.simplify();
              std::vector<Relation> restrictions(2);
              restrictions[1] = Complement(copy(r2));
              restrictions[1].simplify();
              restrictions[0] = r2;
              std::vector<CG_result *> clauses(2);
              clauses[0] = this;
              clauses[1] = this->clone();
              CG_result *cgr = new CG_split(codegen_, active_,
                                            restrictions, clauses);
              cgr = cgr->recompute(active_, copy(known),
                                   copy(restriction));
              return cgr;
            }
        }
        for (int i = 0; i < Rs.size(); i++) {
          Relation r = Approximate(copy(Rs[i]));
          r.simplify(2, 4);
          for (EQ_Iterator e = r.single_conjunct()->EQs(); e; e++)
            if ((*e).get_coef(r.input_var(level_)) != 0) {
              Relation r2 = Relation::True(num_level());
              r2.and_with_GEQ(*e);
              r2.simplify();
              std::vector<Relation> restrictions(2);
              if ((*e).get_coef(r.input_var(level_)) > 0) {
                restrictions[0] = Complement(copy(r2));
                restrictions[0].simplify();
                restrictions[1] = r2;
              } else {
                restrictions[0] = r2;
                restrictions[1] = Complement(copy(r2));
                restrictions[1].simplify();
              }
              std::vector<CG_result *> clauses(2);
              clauses[0] = this;
              clauses[1] = this->clone();
              CG_result *cgr = new CG_split(codegen_, active_,
                                            restrictions, clauses);
              cgr = cgr->recompute(active_, copy(known),
                                   copy(restriction));
              return cgr;
            }
        }
      }
      
      if (!has_lb && !has_ub)
        throw codegen_error(
                            "can't find any bound at loop level " + to_string(level_));
      else if (!has_lb)
        throw codegen_error(
                            "can't find lower bound at loop level "
                            + to_string(level_));
      else if (!has_ub)
        throw codegen_error(
                            "can't find upper bound at loop level "
                            + to_string(level_));
    }
    bounds_.copy_names(hull);
    bounds_.setup_names();
    
    // additional guard/stride condition extraction
    if (needLoop_) {
      Relation cur_known = Intersection(copy(bounds_), copy(known_));
      cur_known.simplify();
      hull = Gist(hull, copy(cur_known), 1);
      
      std::pair<EQ_Handle, Variable_ID> result = find_simplest_stride(hull,
                                                                      hull.set_var(level_));
      if (result.second != NULL)
        if (abs(result.first.get_coef(hull.set_var(level_))) == 1) {
          F_Exists *f_exists = bounds_.and_with_and()->add_exists();
          F_And *f_root = f_exists->add_and();
          std::map<Variable_ID, Variable_ID> exists_mapping;
          EQ_Handle h = f_root->add_EQ();
          for (Constr_Vars_Iter cvi(result.first); cvi; cvi++) {
            Variable_ID v = cvi.curr_var();
            switch (v->kind()) {
            case Input_Var:
              h.update_coef(bounds_.input_var(v->get_position()),
                            cvi.curr_coef());
              break;
            case Wildcard_Var: {
              Variable_ID v2;
              if (v == result.second)
                v2 = f_exists->declare();
              else
                v2 = replicate_floor_definition(hull, v, bounds_,
                                                f_exists, f_root, exists_mapping);
              h.update_coef(v2, cvi.curr_coef());
              break;
            }
            case Global_Var: {
              Global_Var_ID g = v->get_global_var();
              Variable_ID v2;
              if (g->arity() == 0)
                v2 = bounds_.get_local(g);
              else
                v2 = bounds_.get_local(g, v->function_of());
              h.update_coef(v2, cvi.curr_coef());
              break;
            }
            default:
              assert(false);
            }
          }
          h.update_const(result.first.get_const());
        } else {
          // since gist is not powerful enough on modular constraints for now,
          // make an educated guess
          coef_t stride = abs(result.first.get_coef(result.second))
            / gcd(abs(result.first.get_coef(result.second)),
                  abs(
                      result.first.get_coef(
                                            hull.set_var(level_))));
          
          Relation r1(hull.n_inp());
          F_Exists *f_exists = r1.add_and()->add_exists();
          F_And *f_root = f_exists->add_and();
          std::map<Variable_ID, Variable_ID> exists_mapping;
          EQ_Handle h = f_root->add_EQ();
          for (Constr_Vars_Iter cvi(result.first); cvi; cvi++) {
            Variable_ID v = cvi.curr_var();
            switch (v->kind()) {
            case Input_Var:
              h.update_coef(r1.input_var(v->get_position()),
                            cvi.curr_coef());
              break;
            case Wildcard_Var: {
              Variable_ID v2;
              if (v == result.second)
                v2 = f_exists->declare();
              else
                v2 = replicate_floor_definition(hull, v, r1,
                                                f_exists, f_root, exists_mapping);
              h.update_coef(v2, cvi.curr_coef());
              break;
            }
            case Global_Var: {
              Global_Var_ID g = v->get_global_var();
              Variable_ID v2;
              if (g->arity() == 0)
                v2 = r1.get_local(g);
              else
                v2 = r1.get_local(g, v->function_of());
              h.update_coef(v2, cvi.curr_coef());
              break;
            }
            default:
              assert(false);
            }
          }
          h.update_const(result.first.get_const());
          r1.simplify();
          
          bool guess_success = false;
          for (GEQ_Iterator e(bounds_.single_conjunct()->GEQs()); e; e++)
            if ((*e).get_coef(bounds_.set_var(level_)) == 1) {
              Relation r2(hull.n_inp());
              F_Exists *f_exists = r2.add_and()->add_exists();
              F_And *f_root = f_exists->add_and();
              std::map<Variable_ID, Variable_ID> exists_mapping;
              EQ_Handle h = f_root->add_EQ();
              h.update_coef(f_exists->declare(), stride);
              for (Constr_Vars_Iter cvi(*e); cvi; cvi++) {
                Variable_ID v = cvi.curr_var();
                switch (v->kind()) {
                case Input_Var:
                  h.update_coef(r2.input_var(v->get_position()),
                                cvi.curr_coef());
                  break;
                case Wildcard_Var: {
                  Variable_ID v2 = replicate_floor_definition(
                                                              hull, v, r2, f_exists, f_root,
                                                              exists_mapping);
                  h.update_coef(v2, cvi.curr_coef());
                  break;
                }
                case Global_Var: {
                  Global_Var_ID g = v->get_global_var();
                  Variable_ID v2;
                  if (g->arity() == 0)
                    v2 = r2.get_local(g);
                  else
                    v2 = r2.get_local(g, v->function_of());
                  h.update_coef(v2, cvi.curr_coef());
                  break;
                }
                default:
                  assert(false);
                }
              }
              h.update_const((*e).get_const());
              r2.simplify();
              
              if (Gist(copy(r1),
                       Intersection(copy(cur_known), copy(r2)), 1).is_obvious_tautology()
                  && Gist(copy(r2),
                          Intersection(copy(cur_known), copy(r1)),
                          1).is_obvious_tautology()) {
                bounds_ = Intersection(bounds_, r2);
                bounds_.simplify();
                guess_success = true;
                break;
              }
            }
          
          // this is really a stride with non-unit coefficient for this loop variable
          if (!guess_success) {
            // TODO: for stride ax = b mod n it might be beneficial to
            //       generate modular linear equation solver code for
            //       runtime to get the starting position in printRepr,
            //       and stride would be n/gcd(|a|,n), thus this stride
            //       can be put into bounds_ too.
          }
          
        }
      
      hull = Project(hull, hull.set_var(level_));
      hull.simplify(2, 4);
      guard_ = Gist(hull, Intersection(copy(bounds_), copy(known_)), 1);
    }
    // don't generate guard for non-actual loop, postpone it. otherwise
    // redundant if-conditions might be generated since for-loop semantics
    // includes implicit comparison checking.  -- by chun 09/14/10
    else
      guard_ = Relation::True(num_level());
    guard_.copy_names(bounds_);
    guard_.setup_names();
    
    //guard_.simplify();  
    // recursively down the AST
    Relation new_known = Intersection(copy(known),
                                      Intersection(copy(bounds_), copy(guard_)));
    //Anand: IF inspector present in equality and GEQ add the equality constraint to known
    //So as to avoid unnecessary IF condition
    new_known.simplify(2, 4);
    Relation new_restriction = Intersection(copy(restriction),
                                            Intersection(copy(bounds_), copy(guard_)));
    new_restriction.simplify(2, 4);
    body_ = body_->recompute(active_, new_known, new_restriction);
    if (body_ == NULL) {
      delete this;
      return NULL;
    } else
      return this;
  }
  
  int CG_loop::populateDepth() {
    int depth = body_->populateDepth();
    if (needLoop_)
      depth_ = depth + 1;
    else
      depth_ = depth;
    return depth_;
  }
  
  std::pair<CG_result *, Relation> CG_loop::liftOverhead(int depth,
                                                         bool propagate_up) {
    if (depth_ > depth) {
      assert(propagate_up == false);
      std::pair<CG_result *, Relation> result = body_->liftOverhead(depth,
                                                                    false);
      body_ = result.first;
      return std::make_pair(this, Relation::True(num_level()));
    } else { // (depth_ <= depth)
      if (propagate_up) {
        Relation r = pick_one_guard(guard_, level_);
        if (!r.is_obvious_tautology())
          return std::make_pair(this, r);
      }
      
      std::pair<CG_result *, Relation> result;
      if (propagate_up || needLoop_)
        result = body_->liftOverhead(depth, true);
      else
        result = body_->liftOverhead(depth, false);
      body_ = result.first;
      if (result.second.is_obvious_tautology())
        return std::make_pair(this, result.second);
      
      // loop is an assignment, replace this loop variable in overhead condition
      if (!needLoop_) {
        result.second = Intersection(result.second, copy(bounds_));
        result.second = Project(result.second,
                                result.second.set_var(level_));
        result.second.simplify(2, 4);
      }
      
      int max_level = 0;
      bool has_wildcard = false;
      bool direction = true;
      for (EQ_Iterator e(result.second.single_conjunct()->EQs()); e; e++)
        if ((*e).has_wildcards()) {
          if (has_wildcard)
            assert(false);
          else
            has_wildcard = true;
          for (Constr_Vars_Iter cvi(*e); cvi; cvi++)
            if (cvi.curr_var()->kind() == Input_Var
                && cvi.curr_var()->get_position() > max_level)
              max_level = cvi.curr_var()->get_position();
        } else
          assert(false);
      
      if (!has_wildcard) {
        int num_simple_geq = 0;
        for (GEQ_Iterator e(result.second.single_conjunct()->GEQs()); e;
             e++)
          if (!(*e).has_wildcards()) {
            num_simple_geq++;
            for (Constr_Vars_Iter cvi(*e); cvi; cvi++)
              if (cvi.curr_var()->kind() == Input_Var
                  && cvi.curr_var()->get_position() > max_level) {
                max_level = cvi.curr_var()->get_position();
                direction = (cvi.curr_coef() < 0) ? true : false;
              }
          } else {
            has_wildcard = true;
            for (Constr_Vars_Iter cvi(*e); cvi; cvi++)
              if (cvi.curr_var()->kind() == Input_Var
                  && cvi.curr_var()->get_position() > max_level) {
                max_level = cvi.curr_var()->get_position();
              }
          }
        assert(
               (has_wildcard && num_simple_geq == 0) 
               || (!has_wildcard && num_simple_geq == 1));
      }
      
      // check if this is the top loop level for splitting for this overhead
      if (!propagate_up || (has_wildcard && max_level == level_ - 1)
          || (!has_wildcard && max_level == level_)) {
        std::vector<Relation> restrictions(2);
        std::vector<CG_result *> clauses(2);
        int saved_num_level = num_level();
        if (has_wildcard || direction) {
          restrictions[1] = Complement(copy(result.second));
          restrictions[1].simplify();
          clauses[1] = this->clone();
          restrictions[0] = result.second;
          clauses[0] = this;
        } else {
          restrictions[0] = Complement(copy(result.second));
          restrictions[0].simplify();
          clauses[0] = this->clone();
          restrictions[1] = result.second;
          clauses[1] = this;
        }
        CG_result *cgr = new CG_split(codegen_, active_, restrictions,
                                      clauses);
        CG_result *new_cgr = cgr->recompute(active_, copy(known_),
                                            copy(restriction_));
        new_cgr->populateDepth();
        assert(new_cgr==cgr);
        if (static_cast<CG_split *>(new_cgr)->clauses_.size() == 1)
          // infinite recursion detected, bail out
          return std::make_pair(new_cgr, Relation::True(saved_num_level));
        else
          return cgr->liftOverhead(depth, propagate_up);
      } else
        return std::make_pair(this, result.second);
    }
  }
  
  
  
  Relation CG_loop::hoistGuard() {
    
    Relation r = body_->hoistGuard();
    
    // TODO: should bookkeep catched contraints in loop output as enforced and check if anything missing
    // if (!Gist(copy(b), copy(enforced)).is_obvious_tautology()) {
    //   fprintf(stderr, "need to generate extra guard inside the loop\n");
    // }
    
    if (!needLoop_)
      r = Intersection(r, copy(bounds_));
    r = Project(r, r.set_var(level_));
    r = Gist(r, copy(known_), 1);
    
    Relation eliminate_existentials_r;
    Relation eliminate_existentials_known;
    
    eliminate_existentials_r = copy(r);
    if (!r.is_obvious_tautology()) {
      eliminate_existentials_r = Approximate(copy(r));
      eliminate_existentials_r.simplify(2,4);
      eliminate_existentials_known = Approximate(copy(known_));
      eliminate_existentials_known.simplify(2,4);
      
      eliminate_existentials_r = Gist(eliminate_existentials_r, 
                                      eliminate_existentials_known, 1);
    }
    
    
    if (!eliminate_existentials_r.is_obvious_tautology()) {
      // if (!r.is_obvious_tautology()) {
      body_->removeGuard(r);
      guard_ = Intersection(guard_, copy(r));
      guard_.simplify();
    }
    
    return guard_;
  }
  


  void CG_loop::removeGuard(const Relation &guard) {
    known_ = Intersection(known_, copy(guard));
    known_.simplify();
    
    guard_ = Gist(guard_, copy(known_), 1);
    guard_.copy_names(known_);
    guard_.setup_names();
  }
  



  CG_outputRepr *CG_loop::printRepr(int indent, 
                                    CG_outputBuilder *ocg,
                                    const std::vector<CG_outputRepr *> &stmts,
                                    const std::vector<std::pair<CG_outputRepr *, int> > &assigned_on_the_fly,
                                    std::vector<std::map<std::string, std::vector<CG_outputRepr *> > > unin, bool printString) const {
    
    fprintf(stderr, "CG_loop::printRepr() w assigned_on_the_fly gonna call printRepr with more arguments\n"); 
    //int numfly =  assigned_on_the_fly.size();
    //fprintf(stderr, "assigned on the fly  %d\n", numfly );
    //for (int i=0; i<numfly; i++) { 
    //  //fprintf(stderr, "i %d\n", i); 
    //  std::pair<CG_outputRepr *, int>p = assigned_on_the_fly[i];
    //  CG_outputRepr *tr = NULL;
    //  if (p.first != NULL) tr = p.first->clone();
    //  int val = p.second;
    //  //fprintf(stderr, "0x%x   %d\n", tr, val);
    //} 
    
    return printRepr(true, indent, ocg, stmts, assigned_on_the_fly, unin, printString);
  }




  
  CG_outputRepr *CG_loop::printRepr(bool do_print_guard, 
                                    int indent,
                                    CG_outputBuilder *ocg, const std::vector<CG_outputRepr *> &stmts,
                                    const std::vector<std::pair<CG_outputRepr *, int> > &assigned_on_the_fly,
                                    std::vector<std::map<std::string, std::vector<CG_outputRepr *> > > unin, bool printString) const {
    fprintf(stderr, "\n*** CG.cc  CG_loop printrepr with more arguments\n"); 
    
    
    // debugging output 
    int numfly =  assigned_on_the_fly.size();
    fprintf(stderr, "assigned on the fly  %d\n", numfly ); // Anand makes twice as many
    for (int i=0; i<numfly; i++) { 
      //fprintf(stderr, "i %d\n", i); 
      std::pair<CG_outputRepr *, int>p = assigned_on_the_fly[i];
      CG_outputRepr *tr = NULL;
      if (p.first != NULL) tr = p.first->clone();
      int val = p.second;
      //fprintf(stderr, "0x%x   %d\n", tr, val);
    }
    
    //Anand: adding support for Replacing substituted variables within
    //Uninterpreted function symbols or global variables with arity > 0 here
    //--begin
    std::vector<std::pair<CG_outputRepr *, int> > aotf = assigned_on_the_fly;
    int stmt_num = -1;
    for (int s = 0; s < active_.size(); s++)
      if (active_.get(s))
        stmt_num = s;
    
    assert(stmt_num != -1);
    
    CG_outputRepr *guardRepr;
    if (do_print_guard)
      guardRepr = output_guard(ocg, guard_,  aotf, unin[stmt_num]);
    else
      guardRepr = NULL;
    
    fprintf(stderr, "after guard assigned on the fly  %d\n", numfly );
    for (int i=0; i<numfly; i++) { 
      //fprintf(stderr, "i %d\n", i); 
      std::pair<CG_outputRepr *, int>p = assigned_on_the_fly[i];
      CG_outputRepr *tr = NULL;
      if (p.first != NULL) tr = p.first->clone();
      int val = p.second;
      //fprintf(stderr, "0x%x   %d\n", tr, val);
    }
    fprintf(stderr, "done flying\n"); 

    Relation cur_known = Intersection(copy(known_), copy(guard_));
    
    cur_known.simplify();
    fprintf(stderr, "checking needloop\n"); 
    if (needLoop_) {
      fprintf(stderr, "needLoop_\n"); 
      
      if (checkLoopLevel)
        if (level_ == checkLoopLevel)
          if (active_.get(stmtForLoopCheck))
            fillInBounds = true;

      fprintf(stderr, "ctrlRepr = output_loop()\n"); 
      CG_outputRepr *ctrlRepr = output_loop(ocg, bounds_, level_, cur_known,
                                            aotf, unin[stmt_num]);
      
      fillInBounds = false;
      
      fprintf(stderr, "in needLoop_ bodyrepr = \n"); 
      int ind = (guardRepr == NULL) ? indent + 1 : indent + 2;
      CG_outputRepr *bodyRepr = body_->printRepr(ind,
                                                 ocg, 
                                                 stmts,
                                                 aotf, 
                                                 unin, 
                                                 printString);
      CG_outputRepr * loopRepr;
      
      if (guardRepr == NULL)
        loopRepr = ocg->CreateLoop(indent, ctrlRepr, bodyRepr);
      else
        loopRepr = ocg->CreateLoop(indent + 1, ctrlRepr, bodyRepr);
      
      
      if (!smtNonSplitLevels.empty()) {
        fprintf(stderr, "!smtNonSplitLevels.empty()\n"); 
        bool blockLoop = false;
        bool threadLoop = false;
        bool sync = false;
        int firstActiveStmt = -1;
        for (int s = 0; s < active_.size(); s++) {
          if (active_.get(s)) {
            if (firstActiveStmt < 0)
              firstActiveStmt = s;
            //We assume smtNonSplitLevels is only used to mark the first of
            //the block or thread loops to be reduced in CUDA-CHiLL. Here we
            //place some comments to help with final code generation.
            //int idx = smtNonSplitLevels[s].index(level_);
            
            if (s < smtNonSplitLevels.size()) {
              if (smtNonSplitLevels[s].size() > 0)
                if (smtNonSplitLevels[s][0] == level_) {
                  blockLoop = true;
                }
              //Assume every stmt marked with a thread loop index also has a block loop idx
              if (smtNonSplitLevels[s].size() > 1)
                if (smtNonSplitLevels[s][1] == level_) {
                  threadLoop = true;
                }
            }
          }
        }
        if (blockLoop && threadLoop) { 
         fprintf(stderr,
                  "Warning, have %d level more than once in smtNonSplitLevels\n",
                  level_);
          threadLoop = false;
        }
        std::string preferredIdx;

        fprintf(stderr, "loopIdxNames.size() %d\n", loopIdxNames.size());
        for (int i=0; i<loopIdxNames.size(); i++) { 
          fprintf(stderr, "\n"); 
          for (int j=0; j<loopIdxNames[i].size(); j++) { 
            fprintf(stderr, "i %d   j %d %s\n", i, j,loopIdxNames[i][j].c_str() ); 
          }
        } 

        fprintf(stderr, "firstActiveStmt %d\n", firstActiveStmt);
        fprintf(stderr, "loopIdxNames[firstActiveStmt].size() %d\n", loopIdxNames[firstActiveStmt].size()); 
        fprintf(stderr, "level_ %d   /2 %d\n", level_, level_/2); 

        if (loopIdxNames.size()
            && (level_ / 2) - 1 < loopIdxNames[firstActiveStmt].size()) {

          preferredIdx = loopIdxNames[firstActiveStmt][(level_ / 2) - 1];
        }
        for (int s = 0; s < active_.size(); s++) {
          if (active_.get(s)) {
            for (int ii = 0; ii < syncs.size(); ii++) {
              if (syncs[ii].first == s
                  && strcmp(syncs[ii].second.c_str(),
                            preferredIdx.c_str()) == 0) {
                sync = true;
                //printf("FOUND SYNC\n");
              }
              
            }
          }
        }

        if ( preferredIdx.length() != 0) {
          fprintf(stderr, "CG.cc  preferredIdx %s\n", preferredIdx.c_str()); 
        } 

        if (threadLoop || blockLoop || preferredIdx.length() != 0) {
          char buf[1024];
          std::string loop;
          if (blockLoop)
            loop = "blockLoop ";
          if (threadLoop)
            loop = "threadLoop ";

          if ( preferredIdx.length() != 0) {
            fprintf(stderr, "CG.cc adding comment with preferredIdx %s\n", preferredIdx.c_str());
          } 

          if (preferredIdx.length() != 0 && sync) {
            sprintf(buf, "~cuda~ %spreferredIdx: %s sync", loop.c_str(),
                    preferredIdx.c_str());
          } else if (preferredIdx.length() != 0) {
            sprintf(buf, "~cuda~ %spreferredIdx: %s", loop.c_str(),
                    preferredIdx.c_str());
          } else {
            sprintf(buf, "~cuda~ %s", loop.c_str());
          }
          
          
          loopRepr = ocg->CreateAttribute(loopRepr, buf);
        }
        
      }
      if (guardRepr == NULL)
        return loopRepr;
      else
        return ocg->CreateIf(indent, guardRepr, loopRepr, NULL);
    } 
    else {
      fprintf(stderr, "NOT needloop_\n");
      
      std::pair<CG_outputRepr *, std::pair<CG_outputRepr *, int> > result =
        output_assignment(ocg, bounds_, level_, cur_known, aotf, unin[stmt_num]);
      
      //fprintf(stderr, "RESULT  0x%x  0x%x  %d\n", result.first, result.second.first, result.second.second ); 
      
      
      guardRepr = ocg->CreateAnd(guardRepr, result.first);
      //fprintf(stderr, "RESULT  0x%x  0x%x  %d\n", result.first, result.second.first, result.second.second ); 
      
      //fprintf(stderr, "after guardRepr assigned on the fly  %d\n", numfly );
      for (int i=0; i<numfly; i++) { 
        //fprintf(stderr, "i %d\n", i); 
        std::pair<CG_outputRepr *, int>p = assigned_on_the_fly[i];
        CG_outputRepr *tr = NULL;
        if (p.first != NULL) tr = p.first->clone();
        int val = p.second;
        //fprintf(stderr, "0x%x   %d\n", tr, val);
      } 
      
      
      if (result.second.second < CodeGen::var_substitution_threshold) {
        //fprintf(stderr, "var_substitution_threshold  %d < %d    level_ = %d\n", result.second.second, CodeGen::var_substitution_threshold, level_); 
        std::vector<std::pair<CG_outputRepr *, int> > aotf =
          assigned_on_the_fly;
        aotf[level_ - 1] = result.second;
        //fprintf(stderr, "RESULT  0x%x  second 0x%x  %d\n", result.first, result.second.first, result.second.second ); 
        
        if(!printString) {
          for (std::map<std::string, std::vector<CG_outputRepr *> >::iterator i =
                 unin[stmt_num].begin(); i != unin[stmt_num].end(); i++) {
            
            
            std::vector<CG_outputRepr *> to_push;
            for (int j = 0; j < i->second.size(); j++) {
              std::string index =
                const_cast<CG_loop *>(this)->bounds_.set_var(level_)->name();
              std::vector<std::string> loop_vars;
              loop_vars.push_back(index);
              std::vector<CG_outputRepr *> subs;
              subs.push_back(result.second.first->clone());
              CG_outputRepr * new_repr = ocg->CreateSubstitutedStmt(0,
                                                                    i->second[j]->clone(), loop_vars, subs);
              to_push.push_back(new_repr);
            }
            i->second = to_push;
          } // for 
        } // if 
        
        //fprintf(stderr, "aotf !!\n"); 
        for (int i=0; i<numfly; i++) { 
          //fprintf(stderr, "i %d\n", i); 
          std::pair<CG_outputRepr *, int>p = aotf[i];
          CG_outputRepr *tr = NULL;
          if (p.first != NULL) { tr = p.first->clone();  }
          int val = p.second;
        }
        
        //fprintf(stderr, "\nbodyRepr =\n"); 
        //body_->dump(); // this dies 
        int ind =  (guardRepr == NULL) ? indent : indent + 1; 
        CG_outputRepr *bodyRepr = body_->printRepr(ind, ocg, stmts, aotf, unin,
                                                   printString);

        delete aotf[level_ - 1].first;
        if (guardRepr == NULL)
          return bodyRepr;
        else
          return ocg->CreateIf(indent, guardRepr, bodyRepr, NULL);
      } else {
        //fprintf(stderr, "NOT var_substitution_threshold    gonna call output_ident()\n"); 
        int ind =  (guardRepr == NULL) ? indent : indent + 1;
        CG_outputRepr *assignRepr = ocg->CreateAssignment(
          ind,
          output_ident(ocg, bounds_,
                       const_cast<CG_loop *>(this)->bounds_.set_var(
                         level_), aotf, unin[stmt_num]),
          result.second.first);

        CG_outputRepr *bodyRepr = body_->printRepr(ind, ocg, stmts, aotf, unin, 
                                                   printString);

        if (guardRepr == NULL)
          return ocg->StmtListAppend(assignRepr, bodyRepr);
        else
          return ocg->CreateIf(indent, guardRepr,
                               ocg->StmtListAppend(assignRepr, bodyRepr), NULL);

        /*  DEAD CODE
            std::pair<EQ_Handle, int> result_ = find_simplest_assignment(
            copy(bounds_), copy(bounds_).set_var(level_),
            assigned_on_the_fly);
            bool found_func = false;
            Variable_ID v2;
            int arity;
            for (Constr_Vars_Iter cvi(result_.first); cvi; cvi++)
            if (cvi.curr_var()->kind() == Global_Var) {
            Global_Var_ID g = cvi.curr_var()->get_global_var();
            if ((g->arity() > 0)) {
            
            found_func = true;
            arity = g->arity();
            //copy(R).print();
            v2 = copy(bounds_).get_local(g,
            cvi.curr_var()->function_of());
            
            break;
            }
            }
            
            bool is_array = false;
            if (found_func) {
            
            is_array = ocg->QueryInspectorType(
            v2->get_global_var()->base_name());
            
            }
            if (!found_func || !is_array) {
            
            CG_outputRepr *assignRepr = ocg->CreateAssignment(
            (guardRepr == NULL) ? indent : indent + 1,
            output_ident(ocg, bounds_,
            const_cast<CG_loop *>(this)->bounds_.set_var(
            level_), assigned_on_the_fly),
            result.second.first);
            
            CG_outputRepr *bodyRepr = body_->printRepr(
            (guardRepr == NULL) ? indent : indent + 1, ocg, stmts,
            assigned_on_the_fly);
            if (guardRepr == NULL)
            return ocg->StmtListAppend(assignRepr, bodyRepr);
            else
            return ocg->CreateIf(indent, guardRepr,
            ocg->StmtListAppend(assignRepr, bodyRepr), NULL);
            
            } else {
            
            std::vector<CG_outputRepr *> index_expr;
            
            CG_outputRepr* lb = ocg->CreateArrayRefExpression(
            v2->get_global_var()->base_name(),
            output_ident(ocg, bounds_,
            const_cast<CG_loop *>(this)->bounds_.set_var(2),
            assigned_on_the_fly));
            
            for (int i = arity; i > 1; i--) {
            
            index_expr.push_back(
            ocg->CreateArrayRefExpression(
            v2->get_global_var()->base_name(),
            output_ident(ocg, bounds_,
            const_cast<CG_loop *>(this)->bounds_.set_var(
            2 * i),
            assigned_on_the_fly)));
            
            //}
            
            }
            
            CG_outputRepr *ub;
            if (index_expr.size() > 1)
            ub = ocg->CreateInvoke("max", index_expr);
            else
            ub = index_expr[0];
            CG_outputRepr *le = ocg->CreateMinus(ub, ocg->CreateInt(1));
            CG_outputRepr *inductive = ocg->CreateInductive(
            output_ident(ocg, bounds_,
            const_cast<CG_loop *>(this)->bounds_.set_var(
            level_), assigned_on_the_fly), lb, le,
            NULL);
            
            CG_outputRepr *bodyRepr = body_->printRepr(
            (guardRepr == NULL) ? indent : indent + 1, ocg, stmts,
            assigned_on_the_fly);
            
            if (guardRepr == NULL) {
            return ocg->CreateLoop(indent, inductive, bodyRepr);
            } else
            return ocg->CreateIf(indent, guardRepr,
            ocg->CreateLoop(indent + 1, inductive, bodyRepr),
            NULL);
            }
        */      
      }
    }
  }
  

/*
//Protonu--adding a helper function to get the ids of the nested loops
//to help with OpenMP code generation
  std::vector<std::string> get_idxs( CG_result * cgl, std::vector<std::string>& idxs)
  {
    CG_loop * _lp;
    CG_split *_sp;
    
    _lp = dynamic_cast<CG_loop *>(cgl);
    _sp = dynamic_cast<CG_split *>(cgl);
    
    if(_lp){
      if (_lp->needLoop_)
        idxs.push_back((copy(_lp->bounds_).set_var(_lp->level_))->name());
      if (_lp->depth() > 0 ){
        cgl = _lp->body_;
        get_idxs(cgl, idxs);
      }
      if (_lp->depth() == 0)
        return idxs;
    }
    if(_sp){
      for(int i=0; i<_sp->clauses_.size(); i++)
      {
        get_idxs(_sp->clauses_[i], idxs);
      }
    }
    
    return idxs;
    
  }
//end.
*/


  CG_result *CG_loop::clone() const {
    //fprintf(stderr, "CG_loop::clone()\n"); 
    return new CG_loop(codegen_, active_, level_, body_->clone());
  }
  
  void CG_loop::dump(int indent) const {
    std::string prefix;
    for (int i = 0; i < indent; i++)
      prefix += "  ";
    std::cout << prefix << "LOOP (level " << level_ << "): " << active_
              << std::endl;
    std::cout << prefix << "known: ";
    const_cast<CG_loop *>(this)->known_.print();
    std::cout << prefix << "restriction: ";
    const_cast<CG_loop *>(this)->restriction_.print();
    std::cout << prefix << "bounds: ";
    const_cast<CG_loop *>(this)->bounds_.print();
    std::cout << prefix << "guard: ";
    const_cast<CG_loop *>(this)->guard_.print();
    body_->dump(indent + 1);
  }
  
  //-----------------------------------------------------------------------------
  // Class: CG_leaf
  //-----------------------------------------------------------------------------
  
  CG_result* CG_leaf::recompute(const BoolSet<> &parent_active,
                                const Relation &known, const Relation &restriction) {
    active_ &= parent_active;
    known_ = copy(known);
    
    guards_.clear();
    for (BoolSet<>::iterator i = active_.begin(); i != active_.end(); i++) {
      Relation r = Intersection(
                                copy(codegen_->projected_IS_[num_level() - 1][*i]),
                                copy(restriction));
      r.simplify(2, 4);
      if (!r.is_upper_bound_satisfiable())
        active_.unset(*i);
      else {
        r = Gist(r, copy(known), 1);
        if (!r.is_obvious_tautology()) {
          guards_[*i] = r;
          guards_[*i].copy_names(known);
          guards_[*i].setup_names();
        }
      }
    }
    
    
    if (active_.empty()) {
      delete this;
      return NULL;
    } else
      return this;
  }
  
  std::pair<CG_result *, Relation> CG_leaf::liftOverhead(int depth, bool) {
    if (depth == 0)
      return std::make_pair(this, Relation::True(num_level()));
    
    for (std::map<int, Relation>::iterator i = guards_.begin();
         i != guards_.end(); i++) {
      Relation r = pick_one_guard(i->second);
      if (!r.is_obvious_tautology()) {
        bool has_wildcard = false;
        int max_level = 0;
        for (EQ_Iterator e(r.single_conjunct()->EQs()); e; e++) {
          if ((*e).has_wildcards())
            has_wildcard = true;
          for (Constr_Vars_Iter cvi(*e); cvi; cvi++)
            if (cvi.curr_var()->kind() == Input_Var
                && cvi.curr_var()->get_position() > max_level)
              max_level = cvi.curr_var()->get_position();
        }
        for (GEQ_Iterator e(r.single_conjunct()->GEQs()); e; e++) {
          if ((*e).has_wildcards())
            has_wildcard = true;
          for (Constr_Vars_Iter cvi(*e); cvi; cvi++)
            if (cvi.curr_var()->kind() == Input_Var
                && cvi.curr_var()->get_position() > max_level)
              max_level = cvi.curr_var()->get_position();
        }
        
        if (!(has_wildcard && max_level == codegen_->num_level()))
          return std::make_pair(this, r);
      }
    }
    
    return std::make_pair(this, Relation::True(num_level()));
  }
  
  Relation CG_leaf::hoistGuard() {
    std::vector<Relation> guards;
    for (BoolSet<>::iterator i = active_.begin(); i != active_.end(); i++) {
      std::map<int, Relation>::iterator j = guards_.find(*i);
      if (j == guards_.end()) {
        Relation r = Relation::True(num_level());
        r.copy_names(known_);
        r.setup_names();
        return r;
      } else {
        guards.push_back(j->second);
      }
    }
    
    return SimpleHull(guards, true, true);
  }
  
  void CG_leaf::removeGuard(const Relation &guard) {
    known_ = Intersection(known_, copy(guard));
    known_.simplify();
    
    std::map<int, Relation>::iterator i = guards_.begin();
    while (i != guards_.end()) {
      i->second = Gist(i->second, copy(known_), 1);
      if (i->second.is_obvious_tautology())
        guards_.erase(i++);
      else
        ++i;
    }
  }
  
  CG_outputRepr *CG_leaf::printRepr(int indent, CG_outputBuilder *ocg,
                                    const std::vector<CG_outputRepr *> &stmts,
                                    const std::vector<std::pair<CG_outputRepr *, int> > &assigned_on_the_fly,
                                    std::vector<std::map<std::string, std::vector<CG_outputRepr *> > > unin, 
                                    bool printString) const {
    fprintf(stderr, "CG_leaf::printRepr()\n"); 
    int numfly =  assigned_on_the_fly.size();
    //fprintf(stderr, "assigned on the fly  %d\n", numfly );
    for (int i=0; i<numfly; i++) { 
      //fprintf(stderr, "i %d\n", i); 
      std::pair<CG_outputRepr *, int>p = assigned_on_the_fly[i];
      CG_outputRepr *tr = NULL;
      if (p.first != NULL) tr = p.first->clone();
      int val = p.second;
      //fprintf(stderr, "0x%x   %d\n", tr, val);
    }

    return leaf_print_repr(active_, guards_, NULL, known_, indent, ocg,
                           codegen_->remap_, codegen_->xforms_, stmts, 
                           assigned_on_the_fly, unin);
  }
  


  CG_result *CG_leaf::clone() const {
    //fprintf(stderr, "CG_leaf::clone()\n"); 
    return new CG_leaf(codegen_, active_);
  }
  
  void CG_leaf::dump(int indent) const {
    
    std::string prefix;
    for (int i = 0; i < indent; i++)
      prefix += "  ";
    std::cout << prefix << "LEAF: " << active_ << std::endl;
    std::cout << prefix << "known: ";
    std::cout.flush();
    const_cast<CG_leaf *>(this)->known_.print();
    for (std::map<int, Relation>::const_iterator i = guards_.begin();
         i != guards_.end(); i++) {
      std::cout << prefix << "guard #" << i->first << ":";
      const_cast<Relation &>(i->second).print();
    }
  }
  
}