/************************************************************************
**                                                                     **
**                   The YapTab/YapOr/OPTYap systems                   **
**                                                                     **
** YapTab extends the Yap Prolog engine to support sequential tabling  **
** YapOr extends the Yap Prolog engine to support or-parallelism       **
** OPTYap extends the Yap Prolog engine to support or-parallel tabling **
**                                                                     **
**                                                                     **
**      Yap Prolog was developed at University of Porto, Portugal      **
**                                                                     **
************************************************************************/

/*********************
**      Macros      **
*********************/

#ifdef MODE_GLOBAL_TRIE_ENTRY
#define INCREMENT_GLOBAL_TRIE_REFERENCE(ENTRY)                                                          \
        { register gt_node_ptr entry_node = (gt_node_ptr) (ENTRY);                                      \
 	  TrNode_child(entry_node) = (gt_node_ptr) ((UInt) TrNode_child(entry_node) + 1);  \
	}
#define NEW_SUBGOAL_TRIE_NODE(NODE, ENTRY, CHILD, PARENT, NEXT)        \
        INCREMENT_GLOBAL_TRIE_REFERENCE(ENTRY);                        \
        new_subgoal_trie_node(NODE, ENTRY, CHILD, PARENT, NEXT)
#define NEW_ANSWER_TRIE_NODE(NODE, INSTR, ENTRY, CHILD, PARENT, NEXT)  \
        INCREMENT_GLOBAL_TRIE_REFERENCE(ENTRY);                        \
        new_answer_trie_node(NODE, INSTR, ENTRY, CHILD, PARENT, NEXT)
#define NEW_GLOBAL_TRIE_NODE(NODE, ENTRY, CHILD, PARENT, NEXT)         \
        INCREMENT_GLOBAL_TRIE_REFERENCE(ENTRY);                        \
        new_global_trie_node(NODE, ENTRY, CHILD, PARENT, NEXT)
#else
#define NEW_SUBGOAL_TRIE_NODE(NODE, ENTRY, CHILD, PARENT, NEXT)        \
        new_subgoal_trie_node(NODE, ENTRY, CHILD, PARENT, NEXT)
#define NEW_ANSWER_TRIE_NODE(NODE, INSTR, ENTRY, CHILD, PARENT, NEXT)  \
        new_answer_trie_node(NODE, INSTR, ENTRY, CHILD, PARENT, NEXT)
#define NEW_GLOBAL_TRIE_NODE(NODE, ENTRY, CHILD, PARENT, NEXT)         \
        new_global_trie_node(NODE, ENTRY, CHILD, PARENT, NEXT)
#endif /* MODE_GLOBAL_TRIE_ENTRY */


#ifdef MODE_GLOBAL_TRIE_LOOP
#define SUBGOAL_CHECK_INSERT_ENTRY(TAB_ENT, NODE, ENTRY)                           \
        NODE = global_trie_check_insert_entry(NODE, ENTRY PASS_REGS)
#define ANSWER_CHECK_INSERT_ENTRY(SG_FR, NODE, ENTRY, INSTR)                       \
        NODE = global_trie_check_insert_entry(NODE, ENTRY PASS_REGS)
#else
#define SUBGOAL_CHECK_INSERT_ENTRY(TAB_ENT, NODE, ENTRY)                           \
        NODE = subgoal_trie_check_insert_entry(TAB_ENT, NODE, ENTRY PASS_REGS)
#define ANSWER_CHECK_INSERT_ENTRY(SG_FR, NODE, ENTRY, INSTR)                           \
        NODE = answer_trie_check_insert_entry(SG_FR, NODE, ENTRY, INSTR PASS_REGS)
#endif /* MODE_GLOBAL_TRIE_LOOP */


#ifdef INCLUDE_ANSWER_SEARCH_MODE_DIRECTED
#define ANSWER_SAFE_INSERT_ENTRY(NODE, ENTRY, INSTR)                       \
        { ans_node_ptr new_node;                                           \
          NEW_ANSWER_TRIE_NODE(new_node, INSTR, ENTRY, NULL, NODE, NULL);  \
          TrNode_child(NODE) = new_node;                                   \
          NODE = new_node;                                                 \
        }
#ifdef THREADS
#define INVALIDATE_ANSWER_TRIE_NODE(NODE, SG_FR)        \
        TrNode_next(NODE) = SgFr_invalid_chain(SG_FR);  \
        SgFr_invalid_chain(SG_FR) = NODE
#else
#define INVALIDATE_ANSWER_TRIE_NODE(NODE, SG_FR)        \
        FREE_ANSWER_TRIE_NODE(NODE)
#endif /* THREADS */
#define INVALIDATE_ANSWER_TRIE_LEAF_NODE(NODE, SG_FR)   \
        TAG_AS_ANSWER_INVALID_NODE(NODE);               \
        TrNode_next(NODE) = SgFr_invalid_chain(SG_FR);  \
        SgFr_invalid_chain(SG_FR) = NODE
#endif /* INCLUDE_ANSWER_SEARCH_MODE_DIRECTED */



/************************************************************************
**                 subgoal_trie_check_insert_(gt)_entry                **
************************************************************************/

#ifdef INCLUDE_SUBGOAL_TRIE_CHECK_INSERT
#ifndef SUBGOAL_TRIE_LOCK_AT_WRITE_LEVEL /* SUBGOAL_TRIE_LOCK_AT_ENTRY_LEVEL || SUBGOAL_TRIE_LOCK_AT_NODE_LEVEL || ! YAPOR */
#ifdef MODE_GLOBAL_TRIE_ENTRY
static inline sg_node_ptr subgoal_trie_check_insert_gt_entry(tab_ent_ptr tab_ent, sg_node_ptr parent_node, Term t USES_REGS) {
#else
static inline sg_node_ptr subgoal_trie_check_insert_entry(tab_ent_ptr tab_ent, sg_node_ptr parent_node, Term t USES_REGS) {
#endif /* MODE_GLOBAL_TRIE_ENTRY */
  sg_node_ptr child_node;

  LOCK_SUBGOAL_NODE(parent_node);
  child_node = TrNode_child(parent_node);
  if (child_node == NULL) {
    NEW_SUBGOAL_TRIE_NODE(child_node, t, NULL, parent_node, NULL);
    TrNode_child(parent_node) = child_node;
    UNLOCK_SUBGOAL_NODE(parent_node);
    return child_node;
  }

  if (! IS_SUBGOAL_TRIE_HASH(child_node)) {
    int count_nodes = 0;
    do {
      if (TrNode_entry(child_node) == t) {
        UNLOCK_SUBGOAL_NODE(parent_node);
        return child_node;
      }
      count_nodes++;
      child_node = TrNode_next(child_node);
    } while (child_node);
    NEW_SUBGOAL_TRIE_NODE(child_node, t, NULL, parent_node, TrNode_child(parent_node));
    count_nodes++;
    if (count_nodes >= MAX_NODES_PER_TRIE_LEVEL) {
      /* alloc a new hash */
      sg_hash_ptr hash;
      sg_node_ptr chain_node, next_node, *bucket;
      new_subgoal_trie_hash(hash, count_nodes, tab_ent);
      chain_node = child_node;
      do {
        bucket = Hash_buckets(hash) + HASH_ENTRY(TrNode_entry(chain_node), BASE_HASH_BUCKETS);
        next_node = TrNode_next(chain_node);
        TrNode_next(chain_node) = *bucket;
        *bucket = chain_node;
        chain_node = next_node;
      } while (chain_node);
      TrNode_child(parent_node) = (sg_node_ptr) hash;
    } else {
      TrNode_child(parent_node) = child_node;
    }
    UNLOCK_SUBGOAL_NODE(parent_node);
    return child_node;
  }

  { /* trie nodes with hashing */
    sg_hash_ptr hash;
    sg_node_ptr *bucket;
    int count_nodes = 0;
    hash = (sg_hash_ptr) child_node;
    bucket = Hash_buckets(hash) + HASH_ENTRY(t, Hash_num_buckets(hash));
    child_node = *bucket;
    while (child_node) {
      if (TrNode_entry(child_node) == t) {
        UNLOCK_SUBGOAL_NODE(parent_node);
        return child_node;
      }
      count_nodes++;
      child_node = TrNode_next(child_node);
    }
    NEW_SUBGOAL_TRIE_NODE(child_node, t, NULL, parent_node, *bucket);
    *bucket = child_node;
    Hash_num_nodes(hash)++;
    count_nodes++;
    if (count_nodes >= MAX_NODES_PER_BUCKET && Hash_num_nodes(hash) > Hash_num_buckets(hash)) {
      /* expand current hash */
      sg_node_ptr chain_node, next_node, *old_bucket, *old_hash_buckets, *new_hash_buckets;
      int num_buckets;
      num_buckets = Hash_num_buckets(hash) * 2;
      ALLOC_BUCKETS(new_hash_buckets, num_buckets);
      old_hash_buckets = Hash_buckets(hash);
      old_bucket = old_hash_buckets + Hash_num_buckets(hash);
      do {
        if (*--old_bucket) {
          chain_node = *old_bucket;
          do {
            bucket = new_hash_buckets + HASH_ENTRY(TrNode_entry(chain_node), num_buckets);
            next_node = TrNode_next(chain_node);
            TrNode_next(chain_node) = *bucket;
            *bucket = chain_node;
            chain_node = next_node;
          } while (chain_node);
        }
      } while (old_bucket != old_hash_buckets);
      Hash_buckets(hash) = new_hash_buckets;
      Hash_num_buckets(hash) = num_buckets;
      FREE_BUCKETS(old_hash_buckets);
    }
    UNLOCK_SUBGOAL_NODE(parent_node);
    return child_node;
  }
}
#else /* SUBGOAL_TRIE_LOCK_AT_WRITE_LEVEL */
#ifdef MODE_GLOBAL_TRIE_ENTRY
static inline sg_node_ptr subgoal_trie_check_insert_gt_entry(tab_ent_ptr tab_ent, sg_node_ptr parent_node, Term t USES_REGS) {
#else
static inline sg_node_ptr subgoal_trie_check_insert_entry(tab_ent_ptr tab_ent, sg_node_ptr parent_node, Term t USES_REGS) {
#endif /* MODE_GLOBAL_TRIE_ENTRY */
  sg_node_ptr child_node;
  sg_hash_ptr hash;

  child_node = TrNode_child(parent_node);
  if (child_node == NULL) {
#ifdef SUBGOAL_TRIE_ALLOC_BEFORE_CHECK
    NEW_SUBGOAL_TRIE_NODE(child_node, t, NULL, parent_node, NULL);
#endif /* SUBGOAL_TRIE_ALLOC_BEFORE_CHECK */
    LOCK_SUBGOAL_NODE(parent_node);
    if (TrNode_child(parent_node)) {
      sg_node_ptr chain_node = TrNode_child(parent_node);
      if (IS_SUBGOAL_TRIE_HASH(chain_node)) {
#ifdef SUBGOAL_TRIE_ALLOC_BEFORE_CHECK
        FREE_SUBGOAL_TRIE_NODE(child_node);
#endif /* SUBGOAL_TRIE_ALLOC_BEFORE_CHECK */
        UNLOCK_SUBGOAL_NODE(parent_node);
        hash = (sg_hash_ptr) chain_node;
        goto subgoal_trie_hash;
      }
      do {
        if (TrNode_entry(chain_node) == t) {
#ifdef SUBGOAL_TRIE_ALLOC_BEFORE_CHECK
          FREE_SUBGOAL_TRIE_NODE(child_node);
#endif /* SUBGOAL_TRIE_ALLOC_BEFORE_CHECK */
          UNLOCK_SUBGOAL_NODE(parent_node);
          return chain_node;
        }
        chain_node = TrNode_next(chain_node);
      } while (chain_node);
#ifdef SUBGOAL_TRIE_ALLOC_BEFORE_CHECK
      TrNode_next(child_node) = TrNode_child(parent_node);
#else
      NEW_SUBGOAL_TRIE_NODE(child_node, t, NULL, parent_node, TrNode_child(parent_node));
    } else {
      NEW_SUBGOAL_TRIE_NODE(child_node, t, NULL, parent_node, NULL);
#endif /* SUBGOAL_TRIE_ALLOC_BEFORE_CHECK */
    }
    TrNode_child(parent_node) = child_node;
    UNLOCK_SUBGOAL_NODE(parent_node);
    return child_node;
  } 

  if (! IS_SUBGOAL_TRIE_HASH(child_node)) {
    sg_node_ptr first_node = child_node;
    int count_nodes = 0;
    do {
      if (TrNode_entry(child_node) == t)
        return child_node;
      count_nodes++;
      child_node = TrNode_next(child_node);
    } while (child_node);
#ifdef SUBGOAL_TRIE_ALLOC_BEFORE_CHECK
    NEW_SUBGOAL_TRIE_NODE(child_node, t, NULL, parent_node, first_node);
#endif /* SUBGOAL_TRIE_ALLOC_BEFORE_CHECK */
    LOCK_SUBGOAL_NODE(parent_node);
    if (first_node != TrNode_child(parent_node)) {
      sg_node_ptr chain_node = TrNode_child(parent_node);
      if (IS_SUBGOAL_TRIE_HASH(chain_node)) {
#ifdef SUBGOAL_TRIE_ALLOC_BEFORE_CHECK
        FREE_SUBGOAL_TRIE_NODE(child_node);
#endif /* SUBGOAL_TRIE_ALLOC_BEFORE_CHECK */
        UNLOCK_SUBGOAL_NODE(parent_node);
        hash = (sg_hash_ptr) chain_node;
        goto subgoal_trie_hash;
      }
      do {
        if (TrNode_entry(chain_node) == t) {
#ifdef SUBGOAL_TRIE_ALLOC_BEFORE_CHECK
          FREE_SUBGOAL_TRIE_NODE(child_node);
#endif /* SUBGOAL_TRIE_ALLOC_BEFORE_CHECK */
          UNLOCK_SUBGOAL_NODE(parent_node);
          return chain_node;
        }
        count_nodes++;
        chain_node = TrNode_next(chain_node);
      } while (chain_node != first_node);
#ifdef SUBGOAL_TRIE_ALLOC_BEFORE_CHECK
      TrNode_next(child_node) = TrNode_child(parent_node);
#else
      NEW_SUBGOAL_TRIE_NODE(child_node, t, NULL, parent_node, TrNode_child(parent_node));
    } else {
      NEW_SUBGOAL_TRIE_NODE(child_node, t, NULL, parent_node, first_node);
#endif /* SUBGOAL_TRIE_ALLOC_BEFORE_CHECK */
    }
    count_nodes++;
    if (count_nodes >= MAX_NODES_PER_TRIE_LEVEL) {
      /* alloc a new hash */
      sg_node_ptr chain_node, next_node, *bucket;
      new_subgoal_trie_hash(hash, count_nodes, tab_ent);
      chain_node = child_node;
      do {
        bucket = Hash_buckets(hash) + HASH_ENTRY(TrNode_entry(chain_node), BASE_HASH_BUCKETS);
        next_node = TrNode_next(chain_node);
        TrNode_next(chain_node) = *bucket;
        *bucket = chain_node;
        chain_node = next_node;
      } while (chain_node);
      TrNode_child(parent_node) = (sg_node_ptr) hash;
    } else {
      TrNode_child(parent_node) = child_node;
    }
    UNLOCK_SUBGOAL_NODE(parent_node);
    return child_node;
  }

  hash = (sg_hash_ptr) child_node;
subgoal_trie_hash:
  { /* trie nodes with hashing */
    sg_node_ptr *bucket, first_node;
    int num_buckets, count_nodes = 0;

    do {
      num_buckets = Hash_num_buckets(hash);
      // __sync_synchronize();
      bucket = Hash_buckets(hash) + HASH_ENTRY(t, num_buckets);
      first_node = child_node = *bucket;
    } while (num_buckets != Hash_num_buckets(hash));
    while (child_node) {
      if (TrNode_entry(child_node) == t)
        return child_node;
      count_nodes++;
      child_node = TrNode_next(child_node);
    }
#ifdef SUBGOAL_TRIE_ALLOC_BEFORE_CHECK
    NEW_SUBGOAL_TRIE_NODE(child_node, t, NULL, parent_node, first_node);
#endif /* SUBGOAL_TRIE_ALLOC_BEFORE_CHECK */
    LOCK_SUBGOAL_NODE(parent_node);
    if (num_buckets != Hash_num_buckets(hash)) {
      /* the hash has been expanded */ 
#ifdef SUBGOAL_TRIE_ALLOC_BEFORE_CHECK
      FREE_SUBGOAL_TRIE_NODE(child_node);
#endif /* SUBGOAL_TRIE_ALLOC_BEFORE_CHECK */
      UNLOCK_SUBGOAL_NODE(parent_node);
      goto subgoal_trie_hash;
    }
    if (first_node != *bucket) {
      sg_node_ptr chain_node = *bucket;
      do {
        if (TrNode_entry(chain_node) == t) {
#ifdef SUBGOAL_TRIE_ALLOC_BEFORE_CHECK
          FREE_SUBGOAL_TRIE_NODE(child_node);
#endif /* SUBGOAL_TRIE_ALLOC_BEFORE_CHECK */
          UNLOCK_SUBGOAL_NODE(parent_node);
          return chain_node;
        }
        count_nodes++;
        chain_node = TrNode_next(chain_node);
      } while (chain_node != first_node);
#ifdef SUBGOAL_TRIE_ALLOC_BEFORE_CHECK
      TrNode_next(child_node) = *bucket;
#else
      NEW_SUBGOAL_TRIE_NODE(child_node, t, NULL, parent_node, *bucket);
    } else {
      NEW_SUBGOAL_TRIE_NODE(child_node, t, NULL, parent_node, first_node);
#endif /* SUBGOAL_TRIE_ALLOC_BEFORE_CHECK */
    }
    *bucket = child_node;
    Hash_num_nodes(hash)++;
    count_nodes++;
    if (count_nodes >= MAX_NODES_PER_BUCKET && Hash_num_nodes(hash) > Hash_num_buckets(hash)) {
      /* expand current hash */ 
      sg_node_ptr chain_node, next_node, *old_bucket, *old_hash_buckets, *new_hash_buckets;
      num_buckets = Hash_num_buckets(hash) * 2;
      ALLOC_BUCKETS(new_hash_buckets, num_buckets);
      old_hash_buckets = Hash_buckets(hash);
      old_bucket = old_hash_buckets + Hash_num_buckets(hash);
      do {
        if (*--old_bucket) {
          chain_node = *old_bucket;
          do {
            bucket = new_hash_buckets + HASH_ENTRY(TrNode_entry(chain_node), num_buckets);
            next_node = TrNode_next(chain_node);
            TrNode_next(chain_node) = *bucket;
            *bucket = chain_node;
            chain_node = next_node;
          } while (chain_node);
        }
      } while (old_bucket != old_hash_buckets);
      Hash_buckets(hash) = new_hash_buckets;
      Hash_num_buckets(hash) = num_buckets;
      FREE_BUCKETS(old_hash_buckets);
    }
    UNLOCK_SUBGOAL_NODE(parent_node);
    return child_node;
  }
}
#endif /* SUBGOAL_TRIE_LOCK_LEVEL */
#endif /* INCLUDE_SUBGOAL_TRIE_CHECK_INSERT */



/************************************************************************
**                 answer_trie_check_insert_(gt)_entry                 **
************************************************************************/

#ifdef INCLUDE_ANSWER_TRIE_CHECK_INSERT
#ifndef ANSWER_TRIE_LOCK_AT_WRITE_LEVEL /* ANSWER_TRIE_LOCK_AT_ENTRY_LEVEL || ANSWER_TRIE_LOCK_AT_NODE_LEVEL || ! YAPOR */
#ifdef MODE_GLOBAL_TRIE_ENTRY
static inline ans_node_ptr answer_trie_check_insert_gt_entry(sg_fr_ptr sg_fr, ans_node_ptr parent_node, Term t, int instr USES_REGS) {
#else
static inline ans_node_ptr answer_trie_check_insert_entry(sg_fr_ptr sg_fr, ans_node_ptr parent_node, Term t, int instr USES_REGS) {
#endif /* MODE_GLOBAL_TRIE_ENTRY */
  ans_node_ptr child_node;

  TABLING_ERROR_CHECKING(answer_trie_check_insert_(gt)_entry, IS_ANSWER_LEAF_NODE(parent_node));
  LOCK_ANSWER_NODE(parent_node);
  child_node = TrNode_child(parent_node);
  if (child_node == NULL) {
    NEW_ANSWER_TRIE_NODE(child_node, instr, t, NULL, parent_node, NULL);
    TrNode_child(parent_node) = child_node;
    UNLOCK_ANSWER_NODE(parent_node);
    return child_node;
  }

  if (! IS_ANSWER_TRIE_HASH(child_node)) {
    int count_nodes = 0;
    do {
      if (TrNode_entry(child_node) == t) {
        UNLOCK_ANSWER_NODE(parent_node);
        return child_node;
      }
      count_nodes++;
      child_node = TrNode_next(child_node);
    } while (child_node);
    NEW_ANSWER_TRIE_NODE(child_node, instr, t, NULL, parent_node, TrNode_child(parent_node));
    count_nodes++;
    if (count_nodes >= MAX_NODES_PER_TRIE_LEVEL) {
      /* alloc a new hash */
      ans_hash_ptr hash;
      ans_node_ptr chain_node, next_node, *bucket;
      new_answer_trie_hash(hash, count_nodes, sg_fr);
      chain_node = child_node;
      do {
        bucket = Hash_buckets(hash) + HASH_ENTRY(TrNode_entry(chain_node), BASE_HASH_BUCKETS);
        next_node = TrNode_next(chain_node);
        TrNode_next(chain_node) = *bucket;
        *bucket = chain_node;
        chain_node = next_node;
      } while (chain_node);
      TrNode_child(parent_node) = (ans_node_ptr) hash;
    } else {
      TrNode_child(parent_node) = child_node;
    }
    UNLOCK_ANSWER_NODE(parent_node);
    return child_node;
  }

  { /* trie nodes with hashing */
    ans_hash_ptr hash;
    ans_node_ptr *bucket;
    int count_nodes = 0;
    hash = (ans_hash_ptr) child_node;
    bucket = Hash_buckets(hash) + HASH_ENTRY(t, Hash_num_buckets(hash));
    child_node = *bucket;
    while (child_node) {
      if (TrNode_entry(child_node) == t) {
        UNLOCK_ANSWER_NODE(parent_node);
        return child_node;
      }
      count_nodes++;
      child_node = TrNode_next(child_node);
    }
    NEW_ANSWER_TRIE_NODE(child_node, instr, t, NULL, parent_node, *bucket);
    *bucket = child_node;
    Hash_num_nodes(hash)++;
    count_nodes++;
    if (count_nodes >= MAX_NODES_PER_BUCKET && Hash_num_nodes(hash) > Hash_num_buckets(hash)) {
      /* expand current hash */ 
      ans_node_ptr chain_node, next_node, *old_bucket, *old_hash_buckets, *new_hash_buckets;
      int num_buckets;
      num_buckets = Hash_num_buckets(hash) * 2;
      ALLOC_BUCKETS(new_hash_buckets, num_buckets);
      old_hash_buckets = Hash_buckets(hash);
      old_bucket = old_hash_buckets + Hash_num_buckets(hash);
      do {
        if (*--old_bucket) {
          chain_node = *old_bucket;
          do {
            bucket = new_hash_buckets + HASH_ENTRY(TrNode_entry(chain_node), num_buckets);
            next_node = TrNode_next(chain_node);
            TrNode_next(chain_node) = *bucket;
            *bucket = chain_node;
            chain_node = next_node;
          } while (chain_node);
        }
      } while (old_bucket != old_hash_buckets);
      Hash_buckets(hash) = new_hash_buckets;
      Hash_num_buckets(hash) = num_buckets;
      FREE_BUCKETS(old_hash_buckets);
    }
    UNLOCK_ANSWER_NODE(parent_node);
    return child_node;
  }
}
#else /* ANSWER_TRIE_LOCK_AT_WRITE_LEVEL */
#ifdef MODE_GLOBAL_TRIE_ENTRY
static inline ans_node_ptr answer_trie_check_insert_gt_entry(sg_fr_ptr sg_fr, ans_node_ptr parent_node, Term t, int instr USES_REGS) {
#else
static inline ans_node_ptr answer_trie_check_insert_entry(sg_fr_ptr sg_fr, ans_node_ptr parent_node, Term t, int instr USES_REGS) {
#endif /* MODE_GLOBAL_TRIE_ENTRY */
  ans_node_ptr child_node;
  ans_hash_ptr hash;

  TABLING_ERROR_CHECKING(answer_trie_check_insert_(gt)_entry, IS_ANSWER_LEAF_NODE(parent_node));
  child_node = TrNode_child(parent_node);
  if (child_node == NULL) {
#ifdef ANSWER_TRIE_ALLOC_BEFORE_CHECK
    NEW_ANSWER_TRIE_NODE(child_node, instr, t, NULL, parent_node, NULL);
#endif /* ANSWER_TRIE_ALLOC_BEFORE_CHECK */
    LOCK_ANSWER_NODE(parent_node);
    if (TrNode_child(parent_node)) {
      ans_node_ptr chain_node = TrNode_child(parent_node);
      if (IS_ANSWER_TRIE_HASH(chain_node)) {
#ifdef ANSWER_TRIE_ALLOC_BEFORE_CHECK
        FREE_ANSWER_TRIE_NODE(child_node);
#endif /* ANSWER_TRIE_ALLOC_BEFORE_CHECK */
        UNLOCK_ANSWER_NODE(parent_node);
        hash = (ans_hash_ptr) chain_node;
        goto answer_trie_hash;
      }
      do {
        if (TrNode_entry(chain_node) == t) {
#ifdef ANSWER_TRIE_ALLOC_BEFORE_CHECK
          FREE_ANSWER_TRIE_NODE(child_node);
#endif /* ANSWER_TRIE_ALLOC_BEFORE_CHECK */
          UNLOCK_ANSWER_NODE(parent_node);
          return chain_node;
        }
        chain_node = TrNode_next(chain_node);
      } while (chain_node);
#ifdef ANSWER_TRIE_ALLOC_BEFORE_CHECK
      TrNode_next(child_node) = TrNode_child(parent_node);
#else
      NEW_ANSWER_TRIE_NODE(child_node, instr, t, NULL, parent_node, TrNode_child(parent_node));
    } else {
      NEW_ANSWER_TRIE_NODE(child_node, instr, t, NULL, parent_node, NULL);
#endif /* ANSWER_TRIE_ALLOC_BEFORE_CHECK */
    }
    TrNode_child(parent_node) = child_node;
    UNLOCK_ANSWER_NODE(parent_node);
    return child_node;
  } 

  if (! IS_ANSWER_TRIE_HASH(child_node)) {
    ans_node_ptr first_node = child_node;
    int count_nodes = 0;
    do {
      if (TrNode_entry(child_node) == t)
        return child_node;
      count_nodes++;
      child_node = TrNode_next(child_node);
    } while (child_node);
#ifdef ANSWER_TRIE_ALLOC_BEFORE_CHECK
    NEW_ANSWER_TRIE_NODE(child_node, instr, t, NULL, parent_node, first_node);
#endif /* ANSWER_TRIE_ALLOC_BEFORE_CHECK */
    LOCK_ANSWER_NODE(parent_node);
    if (first_node != TrNode_child(parent_node)) {
      ans_node_ptr chain_node = TrNode_child(parent_node);
      if (IS_ANSWER_TRIE_HASH(chain_node)) {
#ifdef ANSWER_TRIE_ALLOC_BEFORE_CHECK
        FREE_ANSWER_TRIE_NODE(child_node);
#endif /* ANSWER_TRIE_ALLOC_BEFORE_CHECK */
        UNLOCK_ANSWER_NODE(parent_node);
        hash = (ans_hash_ptr) chain_node; 
        goto answer_trie_hash;
      }
      do {
        if (TrNode_entry(chain_node) == t) {
#ifdef ANSWER_TRIE_ALLOC_BEFORE_CHECK
          FREE_ANSWER_TRIE_NODE(child_node);
#endif /* ANSWER_TRIE_ALLOC_BEFORE_CHECK */
          UNLOCK_ANSWER_NODE(parent_node);
          return chain_node;
        }
        count_nodes++;
        chain_node = TrNode_next(chain_node);
      } while (chain_node != first_node);
#ifdef ANSWER_TRIE_ALLOC_BEFORE_CHECK
      TrNode_next(child_node) = TrNode_child(parent_node);
#else
      NEW_ANSWER_TRIE_NODE(child_node, instr, t, NULL, parent_node, TrNode_child(parent_node));
    } else {
      NEW_ANSWER_TRIE_NODE(child_node, instr, t, NULL, parent_node, first_node);
#endif /* ANSWER_TRIE_ALLOC_BEFORE_CHECK */
    }
    count_nodes++;
    if (count_nodes >= MAX_NODES_PER_TRIE_LEVEL) {
      /* alloc a new hash */
      ans_node_ptr chain_node, next_node, *bucket;
      new_answer_trie_hash(hash, count_nodes, sg_fr);
      chain_node = child_node;
      do {
        bucket = Hash_buckets(hash) + HASH_ENTRY(TrNode_entry(chain_node), BASE_HASH_BUCKETS);
        next_node = TrNode_next(chain_node);
        TrNode_next(chain_node) = *bucket;
        *bucket = chain_node;
        chain_node = next_node;
      } while (chain_node);
      TrNode_child(parent_node) = (ans_node_ptr) hash;
    } else {
      TrNode_child(parent_node) = child_node;
    }
    UNLOCK_ANSWER_NODE(parent_node);
    return child_node;
  }

  hash = (ans_hash_ptr) child_node;
answer_trie_hash:
  { /* trie nodes with hashing */
    ans_node_ptr *bucket, first_node;
    int num_buckets, count_nodes = 0;

    do {
      num_buckets = Hash_num_buckets(hash);
      // __sync_synchronize();
      bucket = Hash_buckets(hash) + HASH_ENTRY(t, num_buckets);
      first_node = child_node = *bucket;
    } while (num_buckets != Hash_num_buckets(hash));
    while (child_node) {
      if (TrNode_entry(child_node) == t)
        return child_node;
      count_nodes++;
      child_node = TrNode_next(child_node);
    }
#ifdef ANSWER_TRIE_ALLOC_BEFORE_CHECK
    NEW_ANSWER_TRIE_NODE(child_node, instr, t, NULL, parent_node, first_node);
#endif /* ANSWER_TRIE_ALLOC_BEFORE_CHECK */
    LOCK_ANSWER_NODE(parent_node);
    if (num_buckets != Hash_num_buckets(hash)) {
      /* the hash has been expanded */ 
#ifdef ANSWER_TRIE_ALLOC_BEFORE_CHECK
      FREE_ANSWER_TRIE_NODE(child_node);
#endif /* ANSWER_TRIE_ALLOC_BEFORE_CHECK */
      UNLOCK_ANSWER_NODE(parent_node);
      goto answer_trie_hash;
    }
    if (first_node != *bucket) {
      ans_node_ptr chain_node = *bucket;
      do {
        if (TrNode_entry(chain_node) == t) {
#ifdef ANSWER_TRIE_ALLOC_BEFORE_CHECK
          FREE_ANSWER_TRIE_NODE(child_node);
#endif /* ANSWER_TRIE_ALLOC_BEFORE_CHECK */
          UNLOCK_ANSWER_NODE(parent_node);
          return chain_node;
        }
        count_nodes++;
        chain_node = TrNode_next(chain_node);
      } while (chain_node != first_node);
#ifdef ANSWER_TRIE_ALLOC_BEFORE_CHECK
      TrNode_next(child_node) = *bucket;
#else
      NEW_ANSWER_TRIE_NODE(child_node, instr, t, NULL, parent_node, *bucket);
    } else {
      NEW_ANSWER_TRIE_NODE(child_node, instr, t, NULL, parent_node, first_node);
#endif /* ANSWER_TRIE_ALLOC_BEFORE_CHECK */
    }
    *bucket = child_node;
    Hash_num_nodes(hash)++;
    count_nodes++;
    if (count_nodes >= MAX_NODES_PER_BUCKET && Hash_num_nodes(hash) > Hash_num_buckets(hash)) {
      /* expand current hash */
      ans_node_ptr chain_node, next_node, *old_bucket, *old_hash_buckets, *new_hash_buckets;
      num_buckets = Hash_num_buckets(hash) * 2;
      ALLOC_BUCKETS(new_hash_buckets, num_buckets);
      old_hash_buckets = Hash_buckets(hash);
      old_bucket = old_hash_buckets + Hash_num_buckets(hash);
      do {
        if (*--old_bucket) {
          chain_node = *old_bucket;
          do {
            bucket = new_hash_buckets + HASH_ENTRY(TrNode_entry(chain_node), num_buckets);
            next_node = TrNode_next(chain_node);
            TrNode_next(chain_node) = *bucket;
            *bucket = chain_node;
            chain_node = next_node;
          } while (chain_node);
        }
      } while (old_bucket != old_hash_buckets);
      Hash_buckets(hash) = new_hash_buckets;
      Hash_num_buckets(hash) = num_buckets;
      FREE_BUCKETS(old_hash_buckets);
    }
    UNLOCK_ANSWER_NODE(parent_node);
    return child_node;
  }
}
#endif /* ANSWER_TRIE_LOCK_LEVEL */
#endif /* INCLUDE_ANSWER_TRIE_CHECK_INSERT */



/************************************************************************
**                 global_trie_check_insert_(gt)_entry                 **
************************************************************************/

#ifdef INCLUDE_GLOBAL_TRIE_CHECK_INSERT
#ifndef GLOBAL_TRIE_LOCK_AT_WRITE_LEVEL /* GLOBAL_TRIE_LOCK_AT_NODE_LEVEL || ! YAPOR */
#ifdef MODE_GLOBAL_TRIE_ENTRY
static inline gt_node_ptr global_trie_check_insert_gt_entry(gt_node_ptr parent_node, Term t USES_REGS) {
#else
static inline gt_node_ptr global_trie_check_insert_entry(gt_node_ptr parent_node, Term t USES_REGS) {
#endif /* MODE_GLOBAL_TRIE_ENTRY */
  gt_node_ptr child_node;
    
  LOCK_GLOBAL_NODE(parent_node);
  child_node = TrNode_child(parent_node);
  if (child_node == NULL) {
    NEW_GLOBAL_TRIE_NODE(child_node, t, NULL, parent_node, NULL);
    TrNode_child(parent_node) = child_node;
    UNLOCK_GLOBAL_NODE(parent_node);
    return child_node;
  }

  if (! IS_GLOBAL_TRIE_HASH(child_node)) {
    int count_nodes = 0;
    do {
      if (TrNode_entry(child_node) == t) {
        UNLOCK_GLOBAL_NODE(parent_node);
        return child_node;
      }
      count_nodes++;
      child_node = TrNode_next(child_node);
    } while (child_node);
    NEW_GLOBAL_TRIE_NODE(child_node, t, NULL, parent_node, TrNode_child(parent_node));
    count_nodes++;
    if (count_nodes >= MAX_NODES_PER_TRIE_LEVEL) {
      /* alloc a new hash */
      gt_hash_ptr hash;
      gt_node_ptr chain_node, next_node, *bucket;
      new_global_trie_hash(hash, count_nodes);
      chain_node = child_node;
      do {
        bucket = Hash_buckets(hash) + HASH_ENTRY(TrNode_entry(chain_node), BASE_HASH_BUCKETS);
        next_node = TrNode_next(chain_node);
        TrNode_next(chain_node) = *bucket;
        *bucket = chain_node;
        chain_node = next_node;
      } while (chain_node);
      TrNode_child(parent_node) = (gt_node_ptr) hash;
    } else {
      TrNode_child(parent_node) = child_node;
    }
    UNLOCK_GLOBAL_NODE(parent_node);
    return child_node;
  }

  { /* trie nodes with hashing */
    gt_hash_ptr hash;
    gt_node_ptr *bucket;
    int count_nodes = 0;
    hash = (gt_hash_ptr) child_node; 
    bucket = Hash_buckets(hash) + HASH_ENTRY(t, Hash_num_buckets(hash));
    child_node = *bucket;
    while (child_node) { 
      if (TrNode_entry(child_node) == t) {
        UNLOCK_GLOBAL_NODE(parent_node);
        return child_node;
      }
      count_nodes++;
      child_node = TrNode_next(child_node);
    } 
    NEW_GLOBAL_TRIE_NODE(child_node, t, NULL, parent_node, *bucket);
    *bucket = child_node;
    Hash_num_nodes(hash)++;
    count_nodes++;
    if (count_nodes >= MAX_NODES_PER_BUCKET && Hash_num_nodes(hash) > Hash_num_buckets(hash)) {
      /* expand current hash */
      gt_node_ptr chain_node, next_node, *old_bucket, *old_hash_buckets, *new_hash_buckets;
      int num_buckets;
      num_buckets = Hash_num_buckets(hash) * 2;
      ALLOC_BUCKETS(new_hash_buckets, num_buckets);
      old_hash_buckets = Hash_buckets(hash);
      old_bucket = old_hash_buckets + Hash_num_buckets(hash);
      do {
        if (*--old_bucket) {
          chain_node = *old_bucket;
          do {
            bucket = new_hash_buckets + HASH_ENTRY(TrNode_entry(chain_node), num_buckets);
            next_node = TrNode_next(chain_node);
            TrNode_next(chain_node) = *bucket;
            *bucket = chain_node;
            chain_node = next_node;
          } while (chain_node);
        }
      } while (old_bucket != old_hash_buckets);
      Hash_buckets(hash) = new_hash_buckets;
      Hash_num_buckets(hash) = num_buckets;
      FREE_BUCKETS(old_hash_buckets);
    }
    UNLOCK_GLOBAL_NODE(parent_node);
    return child_node;
  }
}
#else /* GLOBAL_TRIE_LOCK_AT_WRITE_LEVEL */
#ifdef MODE_GLOBAL_TRIE_ENTRY
static inline gt_node_ptr global_trie_check_insert_gt_entry(gt_node_ptr parent_node, Term t USES_REGS) {
#else
static inline gt_node_ptr global_trie_check_insert_entry(gt_node_ptr parent_node, Term t USES_REGS) {
#endif /* MODE_GLOBAL_TRIE_ENTRY */
  gt_node_ptr child_node;
  gt_hash_ptr hash;

  child_node = TrNode_child(parent_node);
  if (child_node == NULL) {
#ifdef GLOBAL_TRIE_ALLOC_BEFORE_CHECK
    NEW_GLOBAL_TRIE_NODE(child_node, t, NULL, parent_node, NULL);
#endif /* GLOBAL_TRIE_ALLOC_BEFORE_CHECK */
    LOCK_GLOBAL_NODE(parent_node);
    if (TrNode_child(parent_node)) {
      gt_node_ptr chain_node = TrNode_child(parent_node);
      if (IS_GLOBAL_TRIE_HASH(chain_node)) {
#ifdef GLOBAL_TRIE_ALLOC_BEFORE_CHECK
        FREE_GLOBAL_TRIE_NODE(child_node);
#endif /* GLOBAL_TRIE_ALLOC_BEFORE_CHECK */
        UNLOCK_GLOBAL_NODE(parent_node);
        hash = (gt_hash_ptr) chain_node;
        goto global_trie_hash;
      }
      do {
        if (TrNode_entry(chain_node) == t) {
#ifdef GLOBAL_TRIE_ALLOC_BEFORE_CHECK
          FREE_GLOBAL_TRIE_NODE(child_node);
#endif /* GLOBAL_TRIE_ALLOC_BEFORE_CHECK */
          UNLOCK_GLOBAL_NODE(parent_node);
          return chain_node;
        }
        chain_node = TrNode_next(chain_node);
      } while (chain_node);
#ifdef GLOBAL_TRIE_ALLOC_BEFORE_CHECK
      TrNode_next(child_node) = TrNode_child(parent_node);
#else
      NEW_GLOBAL_TRIE_NODE(child_node, t, NULL, parent_node, TrNode_child(parent_node));
    } else {
      NEW_GLOBAL_TRIE_NODE(child_node, t, NULL, parent_node, NULL);
#endif /* GLOBAL_TRIE_ALLOC_BEFORE_CHECK */
    }
    TrNode_child(parent_node) = child_node;
    UNLOCK_GLOBAL_NODE(parent_node);
    return child_node;
  } 

  if (! IS_GLOBAL_TRIE_HASH(child_node)) {
    gt_node_ptr first_node = child_node;
    int count_nodes = 0;
    do {
      if (TrNode_entry(child_node) == t)
        return child_node;
      count_nodes++;
      child_node = TrNode_next(child_node);
    } while (child_node);
#ifdef GLOBAL_TRIE_ALLOC_BEFORE_CHECK
    NEW_GLOBAL_TRIE_NODE(child_node, t, NULL, parent_node, first_node);
#endif /* GLOBAL_TRIE_ALLOC_BEFORE_CHECK */
    LOCK_GLOBAL_NODE(parent_node);
    if (first_node != TrNode_child(parent_node)) {
      gt_node_ptr chain_node = TrNode_child(parent_node);
      if (IS_GLOBAL_TRIE_HASH(chain_node)) {
#ifdef GLOBAL_TRIE_ALLOC_BEFORE_CHECK
        FREE_GLOBAL_TRIE_NODE(child_node);
#endif /* GLOBAL_TRIE_ALLOC_BEFORE_CHECK */
        UNLOCK_GLOBAL_NODE(parent_node);
        hash = (gt_hash_ptr) chain_node; 
        goto global_trie_hash;
      }
      do {
        if (TrNode_entry(chain_node) == t) {
#ifdef GLOBAL_TRIE_ALLOC_BEFORE_CHECK
          FREE_GLOBAL_TRIE_NODE(child_node);
#endif /* GLOBAL_TRIE_ALLOC_BEFORE_CHECK */
          UNLOCK_GLOBAL_NODE(parent_node);
          return chain_node;
        }
        count_nodes++;
        chain_node = TrNode_next(chain_node);
      } while (chain_node != first_node);
#ifdef GLOBAL_TRIE_ALLOC_BEFORE_CHECK
      TrNode_next(child_node) = TrNode_child(parent_node);
#else
      NEW_GLOBAL_TRIE_NODE(child_node, t, NULL, parent_node, TrNode_child(parent_node));
    } else {
      NEW_GLOBAL_TRIE_NODE(child_node, t, NULL, parent_node, first_node);
#endif /* GLOBAL_TRIE_ALLOC_BEFORE_CHECK */
    }
    count_nodes++;
    if (count_nodes >= MAX_NODES_PER_TRIE_LEVEL) {
      /* alloc a new hash */
      gt_node_ptr chain_node, next_node, *bucket;
      new_global_trie_hash(hash, count_nodes);
      chain_node = child_node;
      do {
        bucket = Hash_buckets(hash) + HASH_ENTRY(TrNode_entry(chain_node), BASE_HASH_BUCKETS);
        next_node = TrNode_next(chain_node);
        TrNode_next(chain_node) = *bucket;
        *bucket = chain_node;
        chain_node = next_node;
      } while (chain_node);
      TrNode_child(parent_node) = (gt_node_ptr) hash;
    } else {
      TrNode_child(parent_node) = child_node;
    }
    UNLOCK_GLOBAL_NODE(parent_node);
    return child_node;
  }

  hash = (gt_hash_ptr) child_node;
global_trie_hash:
  { /* trie nodes with hashing */
    gt_node_ptr *bucket, first_node;
    int num_buckets, count_nodes = 0;

    do {
      num_buckets = Hash_num_buckets(hash);
      // __sync_synchronize();
      bucket = Hash_buckets(hash) + HASH_ENTRY(t, num_buckets);
      first_node = child_node = *bucket;
    } while (num_buckets != Hash_num_buckets(hash));
    while (child_node) {
      if (TrNode_entry(child_node) == t)
        return child_node;
      count_nodes++;
      child_node = TrNode_next(child_node);
    }
#ifdef GLOBAL_TRIE_ALLOC_BEFORE_CHECK
    NEW_GLOBAL_TRIE_NODE(child_node, t, NULL, parent_node, first_node);
#endif /* GLOBAL_TRIE_ALLOC_BEFORE_CHECK */
    LOCK_GLOBAL_NODE(parent_node);
    if (num_buckets != Hash_num_buckets(hash)) {
      /* the hash has been expanded */ 
#ifdef GLOBAL_TRIE_ALLOC_BEFORE_CHECK
      FREE_GLOBAL_TRIE_NODE(child_node);
#endif /* GLOBAL_TRIE_ALLOC_BEFORE_CHECK */
      UNLOCK_GLOBAL_NODE(parent_node);
      goto global_trie_hash;
    }
    if (first_node != *bucket) {
      gt_node_ptr chain_node = *bucket;
      do {
        if (TrNode_entry(chain_node) == t) {
#ifdef GLOBAL_TRIE_ALLOC_BEFORE_CHECK
          FREE_GLOBAL_TRIE_NODE(child_node);
#endif /* GLOBAL_TRIE_ALLOC_BEFORE_CHECK */
          UNLOCK_GLOBAL_NODE(parent_node);
          return chain_node;
        }
        count_nodes++;
        chain_node = TrNode_next(chain_node);
      } while (chain_node != first_node);
#ifdef GLOBAL_TRIE_ALLOC_BEFORE_CHECK
      TrNode_next(child_node) = *bucket;
#else
      NEW_GLOBAL_TRIE_NODE(child_node, t, NULL, parent_node, *bucket);
    } else {
      NEW_GLOBAL_TRIE_NODE(child_node, t, NULL, parent_node, first_node);
#endif /* GLOBAL_TRIE_ALLOC_BEFORE_CHECK */
    }
    *bucket = child_node;
    Hash_num_nodes(hash)++;
    count_nodes++;
    if (count_nodes >= MAX_NODES_PER_BUCKET && Hash_num_nodes(hash) > Hash_num_buckets(hash)) {
      /* expand current hash */ 
      gt_node_ptr chain_node, next_node, *old_bucket, *old_hash_buckets, *new_hash_buckets;
      num_buckets = Hash_num_buckets(hash) * 2;
      ALLOC_BUCKETS(new_hash_buckets, num_buckets);
      old_hash_buckets = Hash_buckets(hash);
      old_bucket = old_hash_buckets + Hash_num_buckets(hash);
      do {
        if (*--old_bucket) {
          chain_node = *old_bucket;
          do {
            bucket = new_hash_buckets + HASH_ENTRY(TrNode_entry(chain_node), num_buckets);
            next_node = TrNode_next(chain_node);
            TrNode_next(chain_node) = *bucket;
            *bucket = chain_node;
            chain_node = next_node;
          } while (chain_node);
        }
      } while (old_bucket != old_hash_buckets);
      Hash_buckets(hash) = new_hash_buckets;
      Hash_num_buckets(hash) = num_buckets;
      FREE_BUCKETS(old_hash_buckets);
    }
    UNLOCK_GLOBAL_NODE(parent_node);
    return child_node;
  }
}
#endif /* GLOBAL_TRIE_LOCK_LEVEL */
#endif /* INCLUDE_GLOBAL_TRIE_CHECK_INSERT */



/************************************************************************
**             subgoal_search(_global_trie)(_terms)_loop               **
************************************************************************/

#ifdef INCLUDE_SUBGOAL_SEARCH_LOOP
#ifdef MODE_GLOBAL_TRIE_LOOP
#ifdef GLOBAL_TRIE_FOR_SUBTERMS
static inline gt_node_ptr subgoal_search_global_trie_terms_loop(Term t, int *subs_arity_ptr, CELL **stack_vars_ptr, CELL *stack_terms USES_REGS) {
#else
static inline gt_node_ptr subgoal_search_global_trie_loop(Term t, int *subs_arity_ptr, CELL **stack_vars_ptr USES_REGS) {
#endif /* GLOBAL_TRIE_FOR_SUBTERMS */
#else
#ifdef MODE_TERMS_LOOP
static inline sg_node_ptr subgoal_search_terms_loop(tab_ent_ptr tab_ent, sg_node_ptr current_node, Term t, int *subs_arity_ptr, CELL **stack_vars_ptr USES_REGS) {
#else
static inline sg_node_ptr subgoal_search_loop(tab_ent_ptr tab_ent, sg_node_ptr current_node, Term t, int *subs_arity_ptr, CELL **stack_vars_ptr USES_REGS) {
#endif /* MODE_TERMS_LOOP */
#endif /* MODE_GLOBAL_TRIE_LOOP */
/************************************************************************
                     ===========
                     |         |
                     |   ...   |
                     |         |
                     -----------
                     |  VAR_N  |  <-- stack_vars
                     -----------           *
                     |   ...   |          /|\
                     -----------           |  subs_arity (N+1)
                     |  VAR_0  |          \|/
                     -----------           *
           YENV -->  |         |
                     -----------
                     |         |
                     |   ...   |
                     |         |
                     ===========
                     |         |
                     |   ...   |
                     |         |
                     -----------
             TR -->  |         |  <-- stack_terms_limit
                     -----------
                     |         |
                     |   ...   |
                     |         |
                     ----------|
                     |  TERM_N |  <-- stack_terms
                     ----------|           *
                     |   ...   |          /|\
                     ----------|           |
                     |  TERM_1 |           |
                     ----------|           |
                     |   NULL  |          \|/
                     ===========           *
 LOCAL_TrailTop -->  |         |
                     -----------
************************************************************************/
#ifdef MODE_GLOBAL_TRIE_LOOP
  gt_node_ptr current_node = GLOBAL_root_gt;
#endif /* MODE_GLOBAL_TRIE_LOOP */
  int subs_arity = *subs_arity_ptr;
  CELL *stack_vars = *stack_vars_ptr;
#if ! defined(MODE_GLOBAL_TRIE_LOOP) || ! defined(GLOBAL_TRIE_FOR_SUBTERMS)
  CELL *stack_terms = (CELL *) LOCAL_TrailTop;
#endif /* ! MODE_GLOBAL_TRIE_LOOP || ! GLOBAL_TRIE_FOR_SUBTERMS */
  CELL *stack_terms_limit = (CELL *) TR;
  AUX_STACK_CHECK_EXPAND(stack_terms, stack_terms_limit + 1);  /* + 1 because initially we stiil haven't done any STACK_POP_DOWN */
  STACK_PUSH_UP(NULL, stack_terms);

#if defined(MODE_GLOBAL_TRIE_LOOP)
  /* for the global trie, it is safe to skip the IsVarTerm() and IsAtomOrIntTerm() tests in the first iteration */
  goto subgoal_search_loop_non_atomic;
#endif /* MODE_GLOBAL_TRIE_LOOP */

#ifdef TRIE_RATIONAL_TERMS
  /* Needed structures, variables to support rational terms */
  term_array Ts;
  void* CyclicTerm;
  term_array_init(&Ts, 10);
#endif /* RATIONAL TERM SUPPORT FOR TRIES */

  do {
    if (IsVarTerm(t)) {
      if (IsTableVarTerm(t)) {
        t = MakeTableVarTerm(VarIndexOfTerm(t));
        SUBGOAL_CHECK_INSERT_ENTRY(tab_ent, current_node, t);
      } else {
        if (subs_arity == MAX_TABLE_VARS)
          Yap_Error(INTERNAL_ERROR, TermNil, "subgoal_search_loop: MAX_TABLE_VARS exceeded");
        STACK_PUSH_UP(t, stack_vars);
        *((CELL *)t) = GLOBAL_table_var_enumerator(subs_arity);
        t = MakeTableVarTerm(subs_arity);
        subs_arity = subs_arity + 1;
        SUBGOAL_CHECK_INSERT_ENTRY(tab_ent, current_node, t);
      }
    } else if (IsAtomOrIntTerm(t)) {
      SUBGOAL_CHECK_INSERT_ENTRY(tab_ent, current_node, t);
#ifdef MODE_TERMS_LOOP
    } else {
      gt_node_ptr entry_node;
#ifdef GLOBAL_TRIE_FOR_SUBTERMS
      entry_node = subgoal_search_global_trie_terms_loop(t, &subs_arity, &stack_vars, stack_terms PASS_REGS);
#else
      entry_node = subgoal_search_global_trie_loop(t, &subs_arity, &stack_vars PASS_REGS);
#endif /* GLOBAL_TRIE_FOR_SUBTERMS */
      current_node = subgoal_trie_check_insert_gt_entry(tab_ent, current_node, (Term) entry_node PASS_REGS);
#else /* ! MODE_TERMS_LOOP */
    } else 
#ifdef TRIE_RATIONAL_TERMS
    if (IsRationalTerm(t)) {
      t = STACK_POP_DOWN(stack_terms);
      SUBGOAL_CHECK_INSERT_ENTRY(tab_ent, current_node, t);
    } else     
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
#if defined(MODE_GLOBAL_TRIE_LOOP)
      /* for the global trie, it is safe to start here in the first iteration */
      subgoal_search_loop_non_atomic:
#endif /* MODE_GLOBAL_TRIE_LOOP */
#ifdef TRIE_COMPACT_PAIRS
    if (IsPairTerm(t)) {
#ifdef TRIE_RATIONAL_TERMS
      CyclicTerm = NULL;
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
      CELL *aux_pair = RepPair(t);
      if (aux_pair == PairTermMark) {
        t = STACK_POP_DOWN(stack_terms);
#ifdef TRIE_RATIONAL_TERMS
        if (IsPairTerm(t) && ! IsRationalTerm(t)) {
          term_array_push(&Ts, (void *) t, (void *) current_node);
#else
        if (IsPairTerm(t)) {
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
          aux_pair = RepPair(t);
          t = Deref(aux_pair[1]);
#ifdef TRIE_RATIONAL_TERMS
          if (IsVarTerm(aux_pair[1]) || IsPairTerm(aux_pair[1])) {
            CyclicTerm = term_array_member(Ts, (void *) t);
          }
          if (CyclicTerm != NULL) {
            STACK_PUSH_UP((Term) CyclicTerm, stack_terms);
            STACK_PUSH_UP((Term) RationalMark, stack_terms);
            STACK_PUSH_UP(AbsPair(PairTermMark), stack_terms);
          } else
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
          if (t == TermNil) {
            SUBGOAL_CHECK_INSERT_ENTRY(tab_ent, current_node, CompactPairEndList);
          } else {
            /* AUX_STACK_CHECK_EXPAND(stack_terms, stack_terms_limit + 2);                   */
            /* AUX_STACK_CHECK_EXPAND is not necessary here because the situation of pushing **
            ** up 3 terms has already initially checked for the CompactPairInit term         */
            STACK_PUSH_UP(t, stack_terms);
            STACK_PUSH_UP(AbsPair(PairTermMark), stack_terms);
          }
#ifdef TRIE_RATIONAL_TERMS
          CyclicTerm = NULL;
          if (IsVarTerm(aux_pair[0]) || IsPairTerm(aux_pair[0]))
            CyclicTerm = term_array_member(Ts, (void *) Deref(aux_pair[0]));
          if (CyclicTerm != NULL) {
            STACK_PUSH_UP((Term) CyclicTerm, stack_terms);          
            STACK_PUSH_UP((Term) RationalMark, stack_terms);          
          } else
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
          STACK_PUSH_UP(Deref(aux_pair[0]), stack_terms);
        } else {
          SUBGOAL_CHECK_INSERT_ENTRY(tab_ent, current_node, CompactPairEndTerm);
          STACK_PUSH_UP(t, stack_terms);
        }
#if defined(MODE_GLOBAL_TRIE_LOOP) && defined(GLOBAL_TRIE_FOR_SUBTERMS)
      } else if (current_node != GLOBAL_root_gt) {
        gt_node_ptr entry_node = subgoal_search_global_trie_terms_loop(t, &subs_arity, &stack_vars, stack_terms PASS_REGS);
        current_node = global_trie_check_insert_gt_entry(current_node, (Term) entry_node PASS_REGS);
#endif /* MODE_GLOBAL_TRIE_LOOP && GLOBAL_TRIE_FOR_SUBTERMS */
      } else {
#ifdef TRIE_RATIONAL_TERMS
        term_array_push(&Ts, (void *) t, (void *) current_node);
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
        SUBGOAL_CHECK_INSERT_ENTRY(tab_ent, current_node, CompactPairInit);
        t = Deref(aux_pair[1]);
#ifdef TRIE_RATIONAL_TERMS
        if (IsVarTerm(aux_pair[1]) || IsPairTerm(aux_pair[1])) {
          CyclicTerm = term_array_member(Ts, (void *) t);
        }
        if (CyclicTerm != NULL) {
          STACK_PUSH_UP((Term) CyclicTerm, stack_terms);          
          STACK_PUSH_UP((Term) RationalMark, stack_terms);          
          STACK_PUSH_UP(AbsPair(PairTermMark), stack_terms);
        } else 
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
        if (t == TermNil) {
          SUBGOAL_CHECK_INSERT_ENTRY(tab_ent, current_node, CompactPairEndList);
        } else {
          AUX_STACK_CHECK_EXPAND(stack_terms, stack_terms_limit + 2);
          STACK_PUSH_UP(t, stack_terms);
          STACK_PUSH_UP(AbsPair(PairTermMark), stack_terms);
        }
#ifdef TRIE_RATIONAL_TERMS
        CyclicTerm = NULL;
        if (IsVarTerm(aux_pair[0]) || IsPairTerm(aux_pair[0]))
          CyclicTerm = term_array_member(Ts, (void *) Deref(aux_pair[0]));
        if (CyclicTerm != NULL) {
          STACK_PUSH_UP((Term) CyclicTerm, stack_terms);          
          STACK_PUSH_UP((Term) RationalMark, stack_terms);          
        } else
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
        STACK_PUSH_UP(Deref(aux_pair[0]), stack_terms);
      }
#if defined(MODE_GLOBAL_TRIE_LOOP) && defined(GLOBAL_TRIE_FOR_SUBTERMS)
    } else if (current_node != GLOBAL_root_gt) {
      gt_node_ptr entry_node = subgoal_search_global_trie_terms_loop(t, &subs_arity, &stack_vars, stack_terms PASS_REGS);
      current_node = global_trie_check_insert_gt_entry(current_node, (Term) entry_node PASS_REGS);
#endif /* MODE_GLOBAL_TRIE_LOOP && GLOBAL_TRIE_FOR_SUBTERMS */
#else /* ! TRIE_COMPACT_PAIRS */
#if defined(MODE_GLOBAL_TRIE_LOOP) && defined(GLOBAL_TRIE_FOR_SUBTERMS)
    if (current_node != GLOBAL_root_gt) {
      gt_node_ptr entry_node = subgoal_search_global_trie_terms_loop(t, &subs_arity, &stack_vars, stack_terms PASS_REGS);
      current_node = global_trie_check_insert_gt_entry(current_node, (Term) entry_node PASS_REGS);
    } else 
#endif /* MODE_GLOBAL_TRIE_LOOP && GLOBAL_TRIE_FOR_SUBTERMS */
    if (IsPairTerm(t)) {
      CELL *aux_pair = RepPair(t);
      SUBGOAL_CHECK_INSERT_ENTRY(tab_ent, current_node, AbsPair(NULL));
      AUX_STACK_CHECK_EXPAND(stack_terms, stack_terms_limit + 1);
      STACK_PUSH_UP(Deref(aux_pair[1]), stack_terms);
      STACK_PUSH_UP(Deref(aux_pair[0]), stack_terms);
#endif /* TRIE_COMPACT_PAIRS */
    } else if (IsApplTerm(t)) {
      Functor f = FunctorOfTerm(t);
      if (f == FunctorDouble) {
        union {
          Term t_dbl[sizeof(Float)/sizeof(Term)];
          Float dbl;
        } u;
        u.dbl = FloatOfTerm(t);
        SUBGOAL_CHECK_INSERT_ENTRY(tab_ent, current_node, AbsAppl((Term *)f));
#if SIZEOF_DOUBLE == 2 * SIZEOF_INT_P
        SUBGOAL_CHECK_INSERT_ENTRY(tab_ent, current_node, u.t_dbl[1]);
#endif /* SIZEOF_DOUBLE x SIZEOF_INT_P */
        SUBGOAL_CHECK_INSERT_ENTRY(tab_ent, current_node, u.t_dbl[0]);
#ifdef MODE_GLOBAL_TRIE_LOOP
        SUBGOAL_CHECK_INSERT_ENTRY(tab_ent, current_node, AbsAppl((Term *)f));
#endif /* MODE_GLOBAL_TRIE_LOOP */
      } else if (f == FunctorLongInt) {
        Int li = LongIntOfTerm(t);
        SUBGOAL_CHECK_INSERT_ENTRY(tab_ent, current_node, AbsAppl((Term *)f));
        SUBGOAL_CHECK_INSERT_ENTRY(tab_ent, current_node, li);
#ifdef MODE_GLOBAL_TRIE_LOOP
        SUBGOAL_CHECK_INSERT_ENTRY(tab_ent, current_node, AbsAppl((Term *)f));
#endif /* MODE_GLOBAL_TRIE_LOOP */
      } else if (f == FunctorBigInt || f == FunctorString) {
	CELL *new = Yap_HeapStoreOpaqueTerm(t);
	SUBGOAL_CHECK_INSERT_ENTRY(tab_ent, current_node, AbsAppl((Term *)f));
	SUBGOAL_CHECK_INSERT_ENTRY(tab_ent, current_node, (CELL)new);
#ifdef MODE_GLOBAL_TRIE_LOOP
	SUBGOAL_CHECK_INSERT_ENTRY(tab_ent, current_node, AbsAppl((Term *)f));
#endif /* MODE_GLOBAL_TRIE_LOOP */	
      } else if (f == FunctorDBRef) {
	Yap_Error(INTERNAL_ERROR, TermNil, "subgoal_search_loop: unsupported type tag FunctorDBRef");
      } else {
#ifdef TRIE_RATIONAL_TERMS
        term_array_push(&Ts, (void *) t, (void *) current_node);
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
        int i;
        CELL *aux_appl = RepAppl(t);
        SUBGOAL_CHECK_INSERT_ENTRY(tab_ent, current_node, AbsAppl((Term *)f));
        AUX_STACK_CHECK_EXPAND(stack_terms, stack_terms_limit + ArityOfFunctor(f) - 1);
        for (i = ArityOfFunctor(f); i >= 1; i--) {
#ifdef TRIE_RATIONAL_TERMS
          CyclicTerm = NULL;
          if (IsVarTerm(aux_appl[i]) || IsApplTerm(aux_appl[i]))
            CyclicTerm = term_array_member(Ts, (void *) Deref(aux_appl[i]));
          if (CyclicTerm != NULL) {
            STACK_PUSH_UP((Term) CyclicTerm, stack_terms);          
            STACK_PUSH_UP((Term) RationalMark, stack_terms);          
          } else
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
            STACK_PUSH_UP(Deref(aux_appl[i]), stack_terms);
        }
      }
    } else {
      Yap_Error(INTERNAL_ERROR, TermNil, "subgoal_search_loop: unknown type tag");
#endif /* MODE_TERMS_LOOP */
    }
    t = STACK_POP_DOWN(stack_terms);
  } while (t);
#ifdef TRIE_RATIONAL_TERMS
  term_array_free(&Ts);
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
  *subs_arity_ptr = subs_arity;
  *stack_vars_ptr = stack_vars;
  return current_node;
}
#endif /* INCLUDE_SUBGOAL_SEARCH_LOOP */



/************************************************************************
**               answer_search(_global_trie)(_terms)_loop              **
************************************************************************/

#ifdef INCLUDE_ANSWER_SEARCH_LOOP
#ifdef MODE_GLOBAL_TRIE_LOOP
#ifdef GLOBAL_TRIE_FOR_SUBTERMS
static inline gt_node_ptr answer_search_global_trie_terms_loop(Term t, int *vars_arity_ptr, CELL *stack_terms USES_REGS) {
#else
static inline gt_node_ptr answer_search_global_trie_loop(Term t, int *vars_arity_ptr USES_REGS) {
#endif /* GLOBAL_TRIE_FOR_SUBTERMS */
#else
#ifdef MODE_TERMS_LOOP
static inline ans_node_ptr answer_search_terms_loop(sg_fr_ptr sg_fr, ans_node_ptr current_node, Term t, int *vars_arity_ptr USES_REGS) {
#else
static inline ans_node_ptr answer_search_loop(sg_fr_ptr sg_fr, ans_node_ptr current_node, Term t, int *vars_arity_ptr USES_REGS) {
#endif /* MODE_TERMS_LOOP */
#endif /* MODE_GLOBAL_TRIE_LOOP */
/************************************************************************
                     ===========
                     |         |
                     |   ...   |
                     |         |
                     -----------
             TR -->  |  VAR_0  |  <-- stack_vars_base
                     -----------           *
                     |   ...   |          /|\
                     -----------           |   vars_arity (N+1)
                     |  VAR_N  |          \|/
                     -----------           *
                     |         |  <-- stack_terms_limit
                     -----------
                     |         |
                     |   ...   |
                     |         |
                     ----------|
                     |  TERM_N |  <-- stack_terms
                     ----------|           *
                     |   ...   |          /|\
                     ----------|           |
                     |  TERM_1 |           |
                     ----------|           |
                     |   NULL  |          \|/
                     ===========           *
 LOCAL_TrailTop -->  |         |
                     -----------
************************************************************************/
#ifdef MODE_GLOBAL_TRIE_LOOP
  gt_node_ptr current_node = GLOBAL_root_gt;
#endif /* MODE_GLOBAL_TRIE_LOOP */
  int vars_arity = *vars_arity_ptr;
#if ! defined(MODE_GLOBAL_TRIE_LOOP) || ! defined(GLOBAL_TRIE_FOR_SUBTERMS)
  CELL *stack_terms = (CELL *) LOCAL_TrailTop;
#endif /* ! MODE_GLOBAL_TRIE_LOOP || ! GLOBAL_TRIE_FOR_SUBTERMS */
  CELL *stack_vars_base = (CELL *) TR;
#define stack_terms_limit (stack_vars_base + vars_arity)
#ifdef TRIE_COMPACT_PAIRS
  int in_pair = 0;
#else
#define in_pair 0
#endif /* TRIE_COMPACT_PAIRS */
  AUX_STACK_CHECK_EXPAND(stack_terms, stack_terms_limit + 1);  /* + 1 because initially we stiil haven't done any STACK_POP_DOWN */
  STACK_PUSH_UP(NULL, stack_terms);

#if defined(MODE_GLOBAL_TRIE_LOOP)
  /* for the global trie, it is safe to skip the IsVarTerm() and IsAtomOrIntTerm() tests in the first iteration */
  goto answer_search_loop_non_atomic;
#endif /* MODE_GLOBAL_TRIE_LOOP */

#ifdef TRIE_RATIONAL_TERMS
  term_array Ts;
  void* CyclicTerm;
  term_array_init(&Ts, 10);
#endif /* RATIONAL TERM SUPPORT FOR TRIES */

  do {
    if (IsVarTerm(t)) {
      t = Deref(t);
      if (IsTableVarTerm(t)) {
        t = MakeTableVarTerm(VarIndexOfTerm(t));
        ANSWER_CHECK_INSERT_ENTRY(sg_fr, current_node, t, _trie_retry_val + in_pair);
      } else {
        if (vars_arity == MAX_TABLE_VARS)
          Yap_Error(INTERNAL_ERROR, TermNil, "answer_search_loop: MAX_TABLE_VARS exceeded");
        stack_vars_base[vars_arity] = t;
        *((CELL *)t) = GLOBAL_table_var_enumerator(vars_arity);
        t = MakeTableVarTerm(vars_arity);
        ANSWER_CHECK_INSERT_ENTRY(sg_fr, current_node, t, _trie_retry_var + in_pair);
        vars_arity = vars_arity + 1;
      }
#ifdef TRIE_COMPACT_PAIRS
      in_pair = 0;
#endif /* TRIE_COMPACT_PAIRS */
    } else if (IsAtomOrIntTerm(t)) {
      ANSWER_CHECK_INSERT_ENTRY(sg_fr, current_node, t, _trie_retry_atom + in_pair);
#ifdef TRIE_COMPACT_PAIRS
      in_pair = 0;
#endif /* TRIE_COMPACT_PAIRS */
#ifdef MODE_TERMS_LOOP
    } else {
      gt_node_ptr entry_node;
#ifdef GLOBAL_TRIE_FOR_SUBTERMS
      entry_node = answer_search_global_trie_terms_loop(t, &vars_arity, stack_terms PASS_REGS);
#else
      entry_node = answer_search_global_trie_loop(t, &vars_arity PASS_REGS);
#endif /*  GLOBAL_TRIE_FOR_SUBTERMS */
      current_node = answer_trie_check_insert_gt_entry(sg_fr, current_node, (Term) entry_node, _trie_retry_gterm + in_pair PASS_REGS);
#else /* ! MODE_TERMS_LOOP */
    } else
#ifdef TRIE_RATIONAL_TERMS
    if (IsRationalTerm(t)) {
      t = STACK_POP_DOWN(stack_terms);
      ANSWER_CHECK_INSERT_ENTRY(sg_fr, current_node, t, _trie_retry_var + in_pair); //TODO create _trie_.._rational
    } else
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
#if defined(MODE_GLOBAL_TRIE_LOOP)
      /* for the global trie, it is safe to start here in the first iteration */
      answer_search_loop_non_atomic:
#endif /* MODE_GLOBAL_TRIE_LOOP */
#ifdef TRIE_COMPACT_PAIRS
    if (IsPairTerm(t)) {
#ifdef TRIE_RATIONAL_TERMS
      CyclicTerm = NULL;    
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
      CELL *aux_pair = RepPair(t);
      if (aux_pair == PairTermMark) {
        t = STACK_POP_DOWN(stack_terms);
#ifdef TRIE_RATIONAL_TERMS
        if (IsPairTerm(t) && ! IsRationalTerm(t)) {
          term_array_push(&Ts, (void *) t, (void *) current_node);
#else
        if (IsPairTerm(t)) {
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
          aux_pair = RepPair(t);
          t = Deref(aux_pair[1]);
#ifdef TRIE_RATIONAL_TERMS
          if (IsVarTerm(aux_pair[1]) || IsPairTerm(aux_pair[1])) {
            CyclicTerm = term_array_member(Ts, (void *) t);
          }
          if (CyclicTerm != NULL) {
            STACK_PUSH_UP((Term) CyclicTerm, stack_terms);          // CyclicTerm 
            STACK_PUSH_UP((Term) RationalMark, stack_terms);
            STACK_PUSH_UP(AbsPair(PairTermMark), stack_terms);
            in_pair = 4;
          } else
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
          if (t == TermNil) {
            ANSWER_CHECK_INSERT_ENTRY(sg_fr, current_node, CompactPairEndList, _trie_retry_pair);
          } else {
            /* AUX_STACK_CHECK_EXPAND(stack_terms, stack_terms_limit + 2);                   */
            /* AUX_STACK_CHECK_EXPAND is not necessary here because the situation of pushing **
            ** up 3 terms has already initially checked for the CompactPairInit term         */
            STACK_PUSH_UP(t, stack_terms);
            STACK_PUSH_UP(AbsPair(PairTermMark), stack_terms);
            in_pair = 4;
          }
#ifdef TRIE_RATIONAL_TERMS
          CyclicTerm = NULL;
          if (IsVarTerm(aux_pair[0]) || IsPairTerm(aux_pair[0]))
            CyclicTerm = term_array_member(Ts, (void *) Deref(aux_pair[0]));
          if (CyclicTerm != NULL) {
            STACK_PUSH_UP((Term) CyclicTerm, stack_terms);          
            STACK_PUSH_UP((Term) RationalMark, stack_terms);          
          } else
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
          STACK_PUSH_UP(Deref(aux_pair[0]), stack_terms);
        } else {
          ANSWER_CHECK_INSERT_ENTRY(sg_fr, current_node, CompactPairEndTerm, _trie_retry_null);
          STACK_PUSH_UP(t, stack_terms);
        }
#if defined(MODE_GLOBAL_TRIE_LOOP) && defined(GLOBAL_TRIE_FOR_SUBTERMS)
      } else if (current_node != GLOBAL_root_gt) {
        gt_node_ptr entry_node = answer_search_global_trie_terms_loop(t, &vars_arity, stack_terms PASS_REGS);
        current_node = global_trie_check_insert_gt_entry(current_node, (Term) entry_node PASS_REGS);
#endif /* MODE_GLOBAL_TRIE_LOOP && GLOBAL_TRIE_FOR_SUBTERMS */
      } else {
#ifdef TRIE_RATIONAL_TERMS
        term_array_push(&Ts, (void *) t, (void *) current_node);
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
        ANSWER_CHECK_INSERT_ENTRY(sg_fr, current_node, CompactPairInit, _trie_retry_null + in_pair);
        t = Deref(aux_pair[1]);
#ifdef TRIE_RATIONAL_TERMS
        if (IsVarTerm(aux_pair[1]) || IsPairTerm(aux_pair[1])) {
          CyclicTerm = term_array_member(Ts, (void *) t);
        }
        if (CyclicTerm != NULL) {
          STACK_PUSH_UP((Term) CyclicTerm, stack_terms);          
          STACK_PUSH_UP((Term) RationalMark, stack_terms);          
          STACK_PUSH_UP(AbsPair(PairTermMark), stack_terms);
          in_pair = 4;
        } else
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
        if (t == TermNil) {
           ANSWER_CHECK_INSERT_ENTRY(sg_fr, current_node, CompactPairEndList, _trie_retry_pair);
           in_pair = 0;
        } else {
          AUX_STACK_CHECK_EXPAND(stack_terms, stack_terms_limit + 2);
          STACK_PUSH_UP(t, stack_terms);
          STACK_PUSH_UP(AbsPair(PairTermMark), stack_terms);
          in_pair = 4;
        }
#ifdef TRIE_RATIONAL_TERMS
        CyclicTerm = NULL;
        if (IsVarTerm(aux_pair[0]) || IsPairTerm(aux_pair[0]))
          CyclicTerm = term_array_member(Ts, (void *) Deref(aux_pair[0]));
        if (CyclicTerm != NULL) {
          STACK_PUSH_UP((Term) CyclicTerm, stack_terms);          
          STACK_PUSH_UP((Term) RationalMark, stack_terms);          
        } else
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
        STACK_PUSH_UP(Deref(aux_pair[0]), stack_terms);
      }
#if defined(MODE_GLOBAL_TRIE_LOOP) && defined(GLOBAL_TRIE_FOR_SUBTERMS)
    } else if (current_node != GLOBAL_root_gt) {
      gt_node_ptr entry_node = answer_search_global_trie_terms_loop(t, &vars_arity, stack_terms PASS_REGS);
      current_node = global_trie_check_insert_gt_entry(current_node, (Term) entry_node PASS_REGS);
#endif /* MODE_GLOBAL_TRIE_LOOP && GLOBAL_TRIE_FOR_SUBTERMS */
#else /* ! TRIE_COMPACT_PAIRS */
#if defined(MODE_GLOBAL_TRIE_LOOP) && defined(GLOBAL_TRIE_FOR_SUBTERMS)
    if (current_node != GLOBAL_root_gt) {
      gt_node_ptr entry_node = answer_search_global_trie_terms_loop(t, &vars_arity, stack_terms PASS_REGS);
      current_node = global_trie_check_insert_gt_entry(current_node, (Term) entry_node PASS_REGS);
    } else 
#endif /* MODE_GLOBAL_TRIE_LOOP && GLOBAL_TRIE_FOR_SUBTERMS */
    if (IsPairTerm(t)) {
      CELL *aux_pair = RepPair(t);
      ANSWER_CHECK_INSERT_ENTRY(sg_fr, current_node, AbsPair(NULL), _trie_retry_pair);
      AUX_STACK_CHECK_EXPAND(stack_terms, stack_terms_limit + 1);
      STACK_PUSH_UP(Deref(aux_pair[1]), stack_terms);
      STACK_PUSH_UP(Deref(aux_pair[0]), stack_terms);
#endif /* TRIE_COMPACT_PAIRS */
    } else if (IsApplTerm(t)) {
      Functor f = FunctorOfTerm(t);
      if (f == FunctorDouble) {
        union {
          Term t_dbl[sizeof(Float)/sizeof(Term)];
          Float dbl;
        } u;
        u.dbl = FloatOfTerm(t);
        ANSWER_CHECK_INSERT_ENTRY(sg_fr, current_node, AbsAppl((Term *)f), _trie_retry_null + in_pair);
#if SIZEOF_DOUBLE == 2 * SIZEOF_INT_P
        ANSWER_CHECK_INSERT_ENTRY(sg_fr, current_node, u.t_dbl[1], _trie_retry_extension);
#endif /* SIZEOF_DOUBLE x SIZEOF_INT_P */
        ANSWER_CHECK_INSERT_ENTRY(sg_fr, current_node, u.t_dbl[0], _trie_retry_extension);
        ANSWER_CHECK_INSERT_ENTRY(sg_fr, current_node, AbsAppl((Term *)f), _trie_retry_double);
      } else if (f == FunctorLongInt) {
	Int li = LongIntOfTerm (t);
	ANSWER_CHECK_INSERT_ENTRY(sg_fr, current_node, AbsAppl((Term *)f), _trie_retry_null + in_pair);
	ANSWER_CHECK_INSERT_ENTRY(sg_fr, current_node, li, _trie_retry_extension);
	ANSWER_CHECK_INSERT_ENTRY(sg_fr, current_node, AbsAppl((Term *)f), _trie_retry_longint);
      } else if (f == FunctorBigInt || FunctorString) {
	CELL *opq = Yap_HeapStoreOpaqueTerm(t);
	ANSWER_CHECK_INSERT_ENTRY(sg_fr, current_node, AbsAppl((Term *)f), _trie_retry_null + in_pair);
	ANSWER_CHECK_INSERT_ENTRY(sg_fr, current_node, (CELL)opq, _trie_retry_extension);
	ANSWER_CHECK_INSERT_ENTRY(sg_fr, current_node, AbsAppl((Term *)f), _trie_retry_bigint);
      } else if (f == FunctorDBRef) {
	Yap_Error(INTERNAL_ERROR, TermNil, "answer_search_loop: unsupported type tag FunctorDBRef");
      } else {
#ifdef TRIE_RATIONAL_TERMS
        term_array_push(&Ts, (void *) t, (void *) current_node);
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
        int i;
        CELL *aux_appl = RepAppl(t);
        ANSWER_CHECK_INSERT_ENTRY(sg_fr, current_node, AbsAppl((Term *)f), _trie_retry_appl + in_pair);
        AUX_STACK_CHECK_EXPAND(stack_terms, stack_terms_limit + ArityOfFunctor(f) - 1);
        for (i = ArityOfFunctor(f); i >= 1; i--) {
#ifdef TRIE_RATIONAL_TERMS
          CyclicTerm = NULL;
          if (IsVarTerm(aux_appl[i]) || IsApplTerm(aux_appl[i]))
            CyclicTerm = term_array_member(Ts, (void *) Deref(aux_appl[i]));
          if (CyclicTerm != NULL) {
            STACK_PUSH_UP((Term) CyclicTerm, stack_terms);
            STACK_PUSH_UP((Term) RationalMark, stack_terms);
          } else
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
          STACK_PUSH_UP(Deref(aux_appl[i]), stack_terms);
        }
      }
#ifdef TRIE_COMPACT_PAIRS
      in_pair = 0;
#endif /* TRIE_COMPACT_PAIRS */
    } else {
      Yap_Error(INTERNAL_ERROR, TermNil, "answer_search_loop: unknown type tag");
#endif /* MODE_TERMS_LOOP */
    }
    t = STACK_POP_DOWN(stack_terms);
  } while (t);
#ifdef TRIE_RATIONAL_TERMS
  term_array_free(&Ts);
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
  *vars_arity_ptr = vars_arity;
  return current_node;

#undef stack_terms_limit
#ifndef TRIE_COMPACT_PAIRS
#undef in_pair
#endif /* TRIE_COMPACT_PAIRS */
}
#endif /* INCLUDE_ANSWER_SEARCH_LOOP */



/**************************************************************
**                   answer_search_min_max                   **
**************************************************************/

#ifdef INCLUDE_ANSWER_SEARCH_MODE_DIRECTED
static inline ans_node_ptr answer_search_min_max(sg_fr_ptr sg_fr, ans_node_ptr current_node, Term t, int mode USES_REGS) {
  ans_node_ptr child_node;
  Term child_term;
  Term trie_value = 0, term_value = t;
  int cmp;

  /* start by computing the current value on the trie (trie_value) */
  child_node = TrNode_child(current_node);
  child_term = TrNode_entry(child_node);
  if (IsIntTerm(child_term)) {
    trie_value = child_term;
  } else if (IsApplTerm(child_term)) {
    Functor f = (Functor) RepAppl(child_term);
    child_node = TrNode_child(child_node);
    if (f == FunctorLongInt) {
      trie_value = MkLongIntTerm( (Int) TrNode_entry(child_node) );
    } else if (f == FunctorDouble) {
      union {
        Term t_dbl[sizeof(Float)/sizeof(Term)];
        Float dbl;
      } u;
      u.t_dbl[0] = TrNode_entry(child_node);
#if SIZEOF_DOUBLE == 2 * SIZEOF_INT_P
      child_node = TrNode_child(child_node);
      u.t_dbl[1] = TrNode_entry(child_node);
#endif /* SIZEOF_DOUBLE x SIZEOF_INT_P */
      trie_value = MkFloatTerm(u.dbl);
    } else if (f == FunctorBigInt) {
      trie_value = AbsAppl( (CELL *) TrNode_entry(child_node) );
    } else
      Yap_Error(INTERNAL_ERROR, TermNil, "answer_search_min_max: invalid arithmetic value");
    child_node = TrNode_child(child_node);
  }

  cmp = Yap_acmp( term_value, trie_value PASS_REGS);
  /* worse answer */
  if ((mode == MODE_DIRECTED_MIN && cmp > 0) || (mode == MODE_DIRECTED_MAX && cmp < 0))
    return NULL;
  /* equal answer */
  if (cmp == 0)
    return child_node;
  /* better answer */
  if (IsAtomOrIntTerm(t)) {
    ANSWER_SAFE_INSERT_ENTRY(current_node, t, _trie_retry_atom);
  } else if (IsApplTerm(t)) {
    Functor f = FunctorOfTerm(t);
    if (f == FunctorDouble) {
      union {
        Term t_dbl[sizeof(Float)/sizeof(Term)];
        Float dbl;
      } u;
      u.dbl = FloatOfTerm(t);
      ANSWER_SAFE_INSERT_ENTRY(current_node, AbsAppl((Term *)f), _trie_retry_null);
#if SIZEOF_DOUBLE == 2 * SIZEOF_INT_P
      ANSWER_SAFE_INSERT_ENTRY(current_node, u.t_dbl[1], _trie_retry_extension);
#endif /* SIZEOF_DOUBLE x SIZEOF_INT_P */
      ANSWER_SAFE_INSERT_ENTRY(current_node, u.t_dbl[0], _trie_retry_extension);
      ANSWER_SAFE_INSERT_ENTRY(current_node, AbsAppl((Term *)f), _trie_retry_double);
    } else if (f == FunctorLongInt) {
      Int li = LongIntOfTerm(t);
      ANSWER_SAFE_INSERT_ENTRY(current_node, AbsAppl((Term *)f), _trie_retry_null);
      ANSWER_SAFE_INSERT_ENTRY(current_node, li, _trie_retry_extension);
      ANSWER_SAFE_INSERT_ENTRY(current_node, AbsAppl((Term *)f), _trie_retry_longint);
    } else if (f == FunctorBigInt) {
      CELL *li = Yap_HeapStoreOpaqueTerm(t);
      ANSWER_SAFE_INSERT_ENTRY(current_node, AbsAppl((Term *)f), _trie_retry_null);
      ANSWER_SAFE_INSERT_ENTRY(current_node, (CELL)li, _trie_retry_extension);
      ANSWER_SAFE_INSERT_ENTRY(current_node, AbsAppl((Term *)f), _trie_retry_bigint);
    }
  }
  return current_node;
}
#endif /* INCLUDE_ANSWER_SEARCH_MODE_DIRECTED */



/**********************************************************
**                   answer_search_sum                   **
**********************************************************/

#ifdef INCLUDE_ANSWER_SEARCH_MODE_DIRECTED
static inline ans_node_ptr answer_search_sum(sg_fr_ptr sg_fr, ans_node_ptr current_node, Term t USES_REGS) {
  ans_node_ptr child_node;
  Term child_term;
  Term trie_value = 0, term_value = t, sum_value = 0;

  /* start by computing the current value on the trie (trie_value) */
  child_node = TrNode_child(current_node);
  child_term = TrNode_entry(child_node);
  if (IsIntTerm(child_term)) {
    trie_value = child_term;
  } else if (IsApplTerm(child_term)) {
    Functor f = (Functor) RepAppl(child_term);
    child_node = TrNode_child(child_node);
    if (f == FunctorLongInt) {
      trie_value = MkLongIntTerm( (Int) TrNode_entry(child_node) );
    } else if (f == FunctorDouble) {
      union {
        Term t_dbl[sizeof(Float)/sizeof(Term)];
        Float dbl;
      } u;
      u.t_dbl[0] = TrNode_entry(child_node);
#if SIZEOF_DOUBLE == 2 * SIZEOF_INT_P
      child_node = TrNode_child(child_node);
      u.t_dbl[1] = TrNode_entry(child_node);
#endif /* SIZEOF_DOUBLE x SIZEOF_INT_P */
      trie_value = MkFloatTerm(u.dbl);
    } else if (f == FunctorBigInt) {
      trie_value = AbsAppl( (CELL *) TrNode_entry(child_node) );
    } else
      Yap_Error(INTERNAL_ERROR, TermNil, "answer_search_min_max: invalid arithmetic value");
    child_node = TrNode_child(child_node);
  }

  sum_value = p_plus(trie_value, term_value PASS_REGS); 
  if (IsAtomOrIntTerm(sum_value)) {
    ANSWER_SAFE_INSERT_ENTRY(current_node, sum_value, _trie_retry_atom);
  } else if (IsApplTerm(sum_value)) {
    Functor f = FunctorOfTerm(sum_value);
    if (f == FunctorDouble) {
      union {
	Term t_dbl[sizeof(Float)/sizeof(Term)];
	Float dbl;
      } u;
      u.dbl = FloatOfTerm(sum_value);
      ANSWER_SAFE_INSERT_ENTRY(current_node, AbsAppl((Term *)f), _trie_retry_null);
#if SIZEOF_DOUBLE == 2 * SIZEOF_INT_P
      ANSWER_SAFE_INSERT_ENTRY(current_node, u.t_dbl[1], _trie_retry_extension);
#endif /* SIZEOF_DOUBLE x SIZEOF_INT_P */
      ANSWER_SAFE_INSERT_ENTRY(current_node, u.t_dbl[0], _trie_retry_extension);
      ANSWER_SAFE_INSERT_ENTRY(current_node, AbsAppl((Term *)f), _trie_retry_double);
    } else if (f == FunctorLongInt) {
      Int li = LongIntOfTerm(sum_value);
      ANSWER_SAFE_INSERT_ENTRY(current_node, AbsAppl((Term *)f), _trie_retry_null);
      ANSWER_SAFE_INSERT_ENTRY(current_node, li, _trie_retry_extension);
      ANSWER_SAFE_INSERT_ENTRY(current_node, AbsAppl((Term *)f), _trie_retry_longint);
    } else if (f == FunctorBigInt) {
      CELL *li = Yap_HeapStoreOpaqueTerm(sum_value);
      ANSWER_SAFE_INSERT_ENTRY(current_node, AbsAppl((Term *)f), _trie_retry_null);
      ANSWER_SAFE_INSERT_ENTRY(current_node, (CELL)li, _trie_retry_extension);
      ANSWER_SAFE_INSERT_ENTRY(current_node, AbsAppl((Term *)f), _trie_retry_bigint);
    }
  }
  return current_node;
}
#endif /* INCLUDE_ANSWER_SEARCH_MODE_DIRECTED */



/***************************************************************
**                   invalidate_answer_trie                   **
***************************************************************/

#ifdef INCLUDE_ANSWER_SEARCH_MODE_DIRECTED
static void invalidate_answer_trie(ans_node_ptr current_node, sg_fr_ptr sg_fr, int position USES_REGS) {
  if (IS_ANSWER_TRIE_HASH(current_node)) {
    ans_hash_ptr hash;
    ans_node_ptr *bucket, *last_bucket;
    hash = (ans_hash_ptr) current_node;
    bucket = Hash_buckets(hash);
    last_bucket = bucket + Hash_num_buckets(hash);
    do {
      current_node = *bucket;
      if (current_node) {
        ans_node_ptr next_node = TrNode_next(current_node);
        if (IS_ANSWER_LEAF_NODE(current_node)) {
          INVALIDATE_ANSWER_TRIE_LEAF_NODE(current_node, sg_fr);
        } else {
          invalidate_answer_trie(TrNode_child(current_node), sg_fr, TRAVERSE_POSITION_FIRST PASS_REGS);
          INVALIDATE_ANSWER_TRIE_NODE(current_node, sg_fr);
        }
        while (next_node) {
          current_node = next_node;
          next_node = TrNode_next(current_node);
          invalidate_answer_trie(current_node, sg_fr, TRAVERSE_POSITION_NEXT PASS_REGS);
        }
      }
    } while (++bucket != last_bucket); 
    if (Hash_next(hash))
      Hash_previous(Hash_next(hash)) = Hash_previous(hash);
    if (Hash_previous(hash))
      Hash_next(Hash_previous(hash)) = Hash_next(hash);
    else
      SgFr_hash_chain(sg_fr) = Hash_next(hash);
    FREE_BUCKETS(Hash_buckets(hash));
    FREE_ANSWER_TRIE_HASH(hash);
  } else {
    if (position == TRAVERSE_POSITION_FIRST) {
      ans_node_ptr next_node = TrNode_next(current_node);
      if (IS_ANSWER_LEAF_NODE(current_node)) {
        INVALIDATE_ANSWER_TRIE_LEAF_NODE(current_node, sg_fr);
      } else {
        invalidate_answer_trie(TrNode_child(current_node), sg_fr, TRAVERSE_POSITION_FIRST PASS_REGS);
        INVALIDATE_ANSWER_TRIE_NODE(current_node, sg_fr);
      }
      while (next_node) {
        current_node = next_node;
        next_node = TrNode_next(current_node);
        invalidate_answer_trie(current_node, sg_fr, TRAVERSE_POSITION_NEXT PASS_REGS);
      }
    } else {
      if (IS_ANSWER_LEAF_NODE(current_node)) {
        INVALIDATE_ANSWER_TRIE_LEAF_NODE(current_node, sg_fr);
      } else {
        invalidate_answer_trie(TrNode_child(current_node), sg_fr, TRAVERSE_POSITION_FIRST PASS_REGS);
        INVALIDATE_ANSWER_TRIE_NODE(current_node, sg_fr);
      }
    }
  }
  return;
}
#endif /* INCLUDE_ANSWER_SEARCH_MODE_DIRECTED */



/************************************************************************
**                   load_(answer|substitution)_loop                   **
************************************************************************/

#ifdef INCLUDE_LOAD_ANSWER_LOOP
#ifdef MODE_GLOBAL_TRIE_LOOP
static inline CELL *load_substitution_loop(gt_node_ptr current_node, int *vars_arity_ptr, CELL *stack_terms USES_REGS) {
#else
static inline CELL *load_answer_loop(ans_node_ptr current_node USES_REGS) {
#endif /* MODE_GLOBAL_TRIE_LOOP */
/************************************************************************
                     ===========
                     |         |
                     |   ...   |
                     |         |
                     -----------
             TR -->  |  VAR_0  |  <-- stack_vars_base
                     -----------           *
                     |   ...   |          /|\
                     -----------           |  vars_arity (N+1)
                     |  VAR_N  |          \|/
                     -----------           *
                     |         |  <-- stack_terms_limit
                     -----------
                     |         |
                     |   ...   |
                     |         |
                     ----------|
                     |  TERM_N |  <-- stack_terms
                     ----------|           *
                     |   ...   |          /|\
                     ----------|           |  stack_terms_pair_offset (TRIE_COMPACT_PAIRS)
                     |  TERM_1 |          \|/
                     ===========           *
 LOCAL_TrailTop -->  |         |  <-- stack_terms_base (TRIE_COMPACT_PAIRS)
                     -----------
************************************************************************/
#ifdef MODE_GLOBAL_TRIE_LOOP
  int vars_arity = *vars_arity_ptr;
#else
  int vars_arity = 0;
  CELL *stack_terms = (CELL *) LOCAL_TrailTop;
#endif /* MODE_GLOBAL_TRIE_LOOP */
  CELL *stack_vars_base = (CELL *) TR;
#define stack_terms_limit (stack_vars_base + vars_arity)
#ifdef TRIE_COMPACT_PAIRS
#define stack_terms_base ((CELL *) LOCAL_TrailTop)
  int stack_terms_pair_offset = 0;
#endif /* TRIE_COMPACT_PAIRS */
  Term t = TrNode_entry(current_node);
#ifdef MODE_GLOBAL_TRIE_LOOP
  current_node = TrNode_parent(current_node);
#else
  current_node = (ans_node_ptr) UNTAG_ANSWER_NODE(TrNode_parent(current_node));
#endif /* MODE_GLOBAL_TRIE_LOOP */

#ifdef TRIE_RATIONAL_TERMS
  term_array Ts;
  void* CyclicTerm;
  term_array_init(&Ts, 10);
  Term RationalTermTMP; // a temporary temp to be used from the rational code
#endif /* RATIONAL TERM SUPPORT FOR TRIES */

  do {
#ifdef TRIE_RATIONAL_TERMS
    CyclicTerm = term_array_member(Ts, (void *) current_node);
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
    if (IsVarTerm(t)) {
#ifdef TRIE_RATIONAL_TERMS
      if (t > VarIndexOfTableTerm(MAX_TABLE_VARS) && TrNode_child((gt_node_ptr) t) != (gt_node_ptr)(1))  { //TODO: substitute the != 1 test to something more appropriate
        /* Rational term */
        RationalTermTMP = (Term) term_array_member(Ts, (void *) t);
        if (RationalTermTMP) {
          /* rational term is assigned a variable already */
          AUX_STACK_CHECK_EXPAND(stack_terms, stack_terms_limit);
          STACK_PUSH_UP(RationalTermTMP, stack_terms);
        } else {
          RationalTermTMP = MkVarTerm();
          STACK_PUSH_UP(RationalTermTMP, stack_terms);
          /* memorize the rational term and assign it a variable */
          term_array_push(&Ts, (void *) t, (void *) RationalTermTMP);
        }
      } else
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
      {
#if ! defined(MODE_GLOBAL_TRIE_LOOP) || defined(GLOBAL_TRIE_FOR_SUBTERMS)
        if (t > VarIndexOfTableTerm(MAX_TABLE_VARS)) {
          stack_terms = load_substitution_loop((gt_node_ptr) t, &vars_arity, stack_terms PASS_REGS);
        } else 
#endif /* ! MODE_GLOBAL_TRIE_LOOP || GLOBAL_TRIE_FOR_SUBTERMS */
        { int var_index = VarIndexOfTableTerm(t);
          AUX_STACK_CHECK_EXPAND(stack_terms, stack_terms_limit - vars_arity + var_index + 1);
          if (var_index >= vars_arity) {
            while (vars_arity < var_index)
              stack_vars_base[vars_arity++] = 0; 
            stack_vars_base[vars_arity++] = MkVarTerm(); 
          } else if (stack_vars_base[var_index] == 0)
            stack_vars_base[var_index] = MkVarTerm(); 
          STACK_PUSH_UP(stack_vars_base[var_index], stack_terms);
        }
      }
    } else if (IsAtomOrIntTerm(t)) {
      AUX_STACK_CHECK_EXPAND(stack_terms, stack_terms_limit);
#ifdef TRIE_RATIONAL_TERMS
      if (CyclicTerm) {
        AUX_STACK_CHECK_EXPAND(stack_terms, stack_terms_limit + 4);
        STACK_PUSH_UP((Term) RationalMark, stack_terms); // Add a rational term marker necessary as we read both ways the stack //
        STACK_PUSH_UP(t, stack_terms);                   // Add the term //
        STACK_PUSH_UP(CyclicTerm, stack_terms);          // Add the variable that the term will unify with //
        STACK_PUSH_UP((Term) RationalMark, stack_terms); // Add a rational term marker necessary as we read both ways the stack //
      } else
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
      STACK_PUSH_UP(t, stack_terms);
    } else if (IsPairTerm(t)) {
#ifdef TRIE_COMPACT_PAIRS
      if (t == CompactPairInit) { 
        Term *stack_aux = stack_terms_base - stack_terms_pair_offset;
        Term head, tail = STACK_POP_UP(stack_aux);
#ifdef TRIE_RATIONAL_TERMS
        if (IsRationalTerm(tail)) {
          Yap_Error(INTERNAL_ERROR, tail, "Rational element of a Rational Term appears as the first Tail of a list");
        }
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
        while (STACK_NOT_EMPTY(stack_aux, stack_terms)) {
          head = STACK_POP_UP(stack_aux);
#ifdef TRIE_RATIONAL_TERMS
          if (IsRationalTerm(head)) {
            head = STACK_POP_UP(stack_aux);     // thats the rational term
            RationalTermTMP = STACK_POP_UP(stack_aux); // that is the variable to unify with
            (void) STACK_POP_UP(stack_aux);            // eat the second rational mark
            tail = MkPairTerm(head, tail);
            Yap_unify(RationalTermTMP, tail);
          } else
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
          tail = MkPairTerm(head, tail);
        }
        stack_terms = stack_terms_base - stack_terms_pair_offset;
        stack_terms_pair_offset = (int) STACK_POP_DOWN(stack_terms);
        STACK_PUSH_UP(tail, stack_terms);
      } else {  /* CompactPairEndList / CompactPairEndTerm */
        Term last;
        AUX_STACK_CHECK_EXPAND(stack_terms, stack_terms_limit + 1);
        last = STACK_POP_DOWN(stack_terms);
#ifdef TRIE_RATIONAL_TERMS
        RationalTermTMP = TermNil;
        if (IsRationalTerm(last)) {                // rather unlikely case the rational term is the last of a list
          RationalTermTMP = STACK_POP_DOWN(stack_terms);  // in this case we need to invert the term with the end of list
          last = STACK_POP_DOWN(stack_terms);      // variable to unify with
          (void) STACK_POP_DOWN(stack_terms);             // eat the second rational mark
        }
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
        STACK_PUSH_UP(stack_terms_pair_offset, stack_terms);
        stack_terms_pair_offset = (int) (stack_terms_base - stack_terms);
        if (t == CompactPairEndList)
          STACK_PUSH_UP(TermNil, stack_terms);
#ifdef TRIE_RATIONAL_TERMS
        if (RationalTermTMP && RationalTermTMP != TermNil) {
          /* most probably this never occurs */
          STACK_PUSH_UP((Term) RationalMark, stack_terms);
          STACK_PUSH_UP(last, stack_terms);
          STACK_PUSH_UP(RationalTermTMP, stack_terms);
          STACK_PUSH_UP((Term) RationalMark, stack_terms);
        } else
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
        STACK_PUSH_UP(last, stack_terms);
      }
#else /* ! TRIE_COMPACT_PAIRS */
      Term head = STACK_POP_DOWN(stack_terms);
      Term tail = STACK_POP_DOWN(stack_terms);
      t = MkPairTerm(head, tail);
      STACK_PUSH_UP(t, stack_terms);
#endif /* TRIE_COMPACT_PAIRS */
    } else if (IsApplTerm(t)) {
      Functor f = (Functor) RepAppl(t);
      if (f == FunctorDouble) {
        union {
          Term t_dbl[sizeof(Float)/sizeof(Term)];
          Float dbl;
        } u;
        t = TrNode_entry(current_node);
        current_node = TrNode_parent(current_node);
        u.t_dbl[0] = t;
#if SIZEOF_DOUBLE == 2 * SIZEOF_INT_P
        t = TrNode_entry(current_node);
        current_node = TrNode_parent(current_node);
        u.t_dbl[1] = t;
#endif /* SIZEOF_DOUBLE x SIZEOF_INT_P */
        current_node = TrNode_parent(current_node);
        t = MkFloatTerm(u.dbl);
      } else if (f == FunctorLongInt) {
	Int li = TrNode_entry(current_node);
	current_node = TrNode_parent(current_node);
	current_node = TrNode_parent(current_node);
	t = MkLongIntTerm(li);
      } else if (f == FunctorBigInt || f == FunctorString) {
	CELL *ptr = (CELL *)TrNode_entry(current_node);
	current_node = TrNode_parent(current_node);
	current_node = TrNode_parent(current_node);
	t = AbsAppl( ptr );
      } else {
        int f_arity = ArityOfFunctor(f);
        t = Yap_MkApplTerm(f, f_arity, stack_terms);
        stack_terms += f_arity;
      }
      AUX_STACK_CHECK_EXPAND(stack_terms, stack_terms_limit);
      STACK_PUSH_UP(t, stack_terms);
    }
#ifdef TRIE_RATIONAL_TERMS
    if (CyclicTerm) {
      RationalTermTMP = STACK_POP_DOWN(stack_terms);
      if IsRationalTerm(RationalTermTMP) {
        //printf("Special Case\n");
      } else if (IsPairTerm(RationalTermTMP)) {
        Yap_unify((Term) CyclicTerm, RationalTermTMP);
      } else if (IsApplTerm(RationalTermTMP)) {
        Yap_unify((Term) CyclicTerm, RationalTermTMP);
      }
      STACK_PUSH_UP(RationalTermTMP, stack_terms);
    }
    RationalTermTMP = TermNil;
    CyclicTerm = NULL;
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
    t = TrNode_entry(current_node);
    current_node = TrNode_parent(current_node);
  } while (current_node);
#ifdef TRIE_RATIONAL_TERMS
  term_array_free(&Ts);
#endif /* RATIONAL TERM SUPPORT FOR TRIES */
#ifdef MODE_GLOBAL_TRIE_LOOP
  *vars_arity_ptr = vars_arity;
#endif /* MODE_GLOBAL_TRIE_LOOP */
  return stack_terms;

#undef stack_terms_limit
#ifdef TRIE_COMPACT_PAIRS
#undef stack_terms_base
#endif /* TRIE_COMPACT_PAIRS */
}
#endif /* INCLUDE_LOAD_ANSWER_LOOP */



/***************************
**      Undef Macros      **
***************************/

#undef INCREMENT_GLOBAL_TRIE_REFERENCE
#undef NEW_SUBGOAL_TRIE_NODE
#undef NEW_ANSWER_TRIE_NODE
#undef NEW_GLOBAL_TRIE_NODE
#undef SUBGOAL_CHECK_INSERT_ENTRY
#undef ANSWER_CHECK_INSERT_ENTRY