// file: $isip/class/search/HierarchicalSearch/hsrch_12.cc
// version: $Id: hsrch_12.cc 9329 2003-10-29 20:40:03Z alphonso $
//

// isip include files
//
#include "HierarchicalSearch.h"

// method: ascend
//
// arguments:
//   Instance* instance: (input) input instance
//
// return: logical error status
//
// this method saves the current symbol in the symbol stack before
// replacing it with the symbol on the history stack
//
boolean HierarchicalSearch::ascend(Instance* instance_a) {

  // declare local variables
  //
  Context* symbol = (Context*)NULL;
  History* history = (History*)NULL;
  History* symbol_stack = (History*)NULL;
  
  // make sure the instance is valid
  //
  if (instance_a == (Instance*)NULL) {
    return Error::handle(name(), L"ascend - instance is null", Error::ARG, __FILE__, __LINE__);
  }

  // if the symbol is null then we cannot ascend
  //
  symbol = instance_a->getSymbol();

  if (symbol == (Context*)NULL) {
    return Error::handle(name(), L"ascend - symbol is null", Error::ARG, __FILE__, __LINE__);
  }

  // output the debugging information
  //
  if (debug_level_d >= Integral::ALL) {
    symbol->print();
  }
    
  // if the history stack is empty or null then we cannot ascend any further
  //
  history = instance_a->getHistory();
  
  if (history == (History*)NULL) {
    return Error::handle(name(), L"ascend - history is null", Error::ARG, __FILE__, __LINE__);
  }
  if (history->isEmpty()) {
    return true;
  }  

  // output the debugging information
  //
  if (debug_level_d >= Integral::ALL) {
    if (!history->isEmpty()) {
      history->peek()->print();
    }
  }
    
  // if the symbol stack is null then we cannot ascend
  //
  symbol_stack = instance_a->getSymbolStack();

  if (symbol_stack == (History*)NULL) {
    return Error::handle(name(), L"ascend - symbol stack is empty", Error::ARG, __FILE__, __LINE__);
  }
  // output the debugging information
  //
  if (debug_level_d >= Integral::ALL) {
    if (!symbol_stack->isEmpty()) {
      symbol_stack->peek()->print();
    }
  }
  
  // push the current symbol on the symbol stack
  //
  symbol_stack = history_pool_d.pushAndAllocate(symbol_stack, symbol);

  // output the debugging information
  //
  if (debug_level_d >= Integral::ALL) {
    if (!symbol_stack->isEmpty()) {
      symbol_stack->peek()->print();
    }
  }

  // has the history been previously generated?
  // if so reuse the history,
  // if not generate a new history and add it to the history pool
  //	  
  instance_a->setSymbolStack(symbol_stack);
  
  // move the symbol on top of the history to the current symbol
  //
  history = history_pool_d.popAndAllocate(history, symbol);

  // output the debugging information
  //
  if (debug_level_d >= Integral::ALL) {
    if (!history->isEmpty()) {
      history->peek()->print();
    }
  }
    
  // the context must exist in the context pool in this case
  //
  instance_a->setSymbol(symbol);
 
  // output the debugging information
  //
  if (debug_level_d >= Integral::ALL) {
    instance_a->getSymbol()->print();
  }
  
  // has the history been previously generated?
  // if so reuse the history,
  // if not generate a new history and add it to the history pool
  //	  
  instance_a->setHistory(history);
  
  // exit gracefully
  //
  return true;
}

// method: descend
//
// arguments:
//   Instance* instance: (input) input instance
//
// return: logical error status
//
// this method saves the current symbol in the symbol stack before
// replacing it with the symbol on the history stack
//
boolean HierarchicalSearch::descend(Instance* instance_a) {

  // declare local variables
  //
  Context* symbol = (Context*)NULL;
  History* history = (History*)NULL;
  History* symbol_stack = (History*)NULL;
  
  // make sure the instance is valid
  //
  if (instance_a == (Instance*)NULL) {
    return Error::handle(name(), L"descend - instance is null", Error::ARG, __FILE__, __LINE__);
  }

  // if the symbol is null then we cannot descend
  //
  symbol = instance_a->getSymbol();

  if (symbol == (Context*)NULL) {
    return Error::handle(name(), L"descend - symbol is null", Error::ARG, __FILE__, __LINE__);
  }

  // output the debugging information
  //
  if (debug_level_d >= Integral::ALL) {
    symbol->print();
  }
  
  // if the history stack null then we cannot descend any further
  //
  history = instance_a->getHistory();
  
  if (history == (History*)NULL) {
    return Error::handle(name(), L"descend - history is null", Error::ARG, __FILE__, __LINE__);
  }

  // output the debugging information
  //
  if (debug_level_d >= Integral::ALL) {
    if (!history->isEmpty()) {
      history->peek()->print();
    }
  }  
  
  // if the symbol stack is null or empty then we cannot descend
  //
  symbol_stack = instance_a->getSymbolStack();

  if (symbol_stack == (History*)NULL) {
    return Error::handle(name(), L"descend - symbol stack is empty", Error::ARG, __FILE__, __LINE__);
  }

  if (symbol_stack->isEmpty()) {
    return true;
  }

  // output the debugging information
  //
  if (debug_level_d >= Integral::ALL) {
    if (!symbol_stack->isEmpty()) {
      symbol_stack->peek()->print();
    }
  }
  
  // push the current symbol on the history stack
  //
  history = history_pool_d.pushAndAllocate(history, symbol);

  // output the debugging information
  //
  if (debug_level_d >= Integral::ALL) {
    if (!history->isEmpty()) {
      history->peek()->print();
    }
  }
  
  // has the history been previously generated?
  // if so reuse the history,
  // if not generate a new history and add it to the history pool
  //	  
  instance_a->setHistory(history);

  // move the symbol on top of the symbol stack to the current symbol
  //
  symbol_stack = history_pool_d.popAndAllocate(symbol_stack, symbol);

  // output the debugging information
  //
  if (debug_level_d >= Integral::ALL) {
    if (!symbol_stack->isEmpty()) {
      symbol_stack->peek()->print();
    }
  }
  
  // the context must exist in the context pool in this case
  //
  instance_a->setSymbol(symbol);
  
  // has the history been previously generated?
  // if so reuse the history,
  // if not generate a new history and add it to the history pool
  //	  
  instance_a->setSymbolStack(symbol_stack);
  
  // exit gracefully
  //
  return true;
}

// method: generateRightContexts
//
// arguments:
//   DoubleLinkedList<Context>& context_list: (output) list of next contexts
//   DoubleLinkedList<Float>& score: (output) transition probabilities
//   const Context* initial_context: (input) initial context to start from
//   long depth: (input) right context length
//   long level: (input) current search level
//
// return: logical error status
//
// generate a list of contexts expanded from the starting context to right
// into the context depth
//
boolean HierarchicalSearch::generateRightContexts(
                                  DoubleLinkedList<Context>& context_list_a,
				  DoubleLinkedList<Float>& score_list_a,
				  Context* initial_context_a,
				  long depth_a, long level_a) {


  // declare local variables
  //
  Float* new_score = (Float*)NULL;  
  Float* curr_score = (Float*)NULL;

  Context* new_context = (Context*)NULL;  
  Context* curr_context = (Context*)NULL;
  
  context_list_a.insertLast(initial_context_a);
  score_list_a.insertLast(new Float(0.0));
  
  // genrate new context lists into the depht of depth_a
  //
  for (long i = 0; i < depth_a; i++) {
    
    // all new contexts will be added at the end of the list so we set
    // the mark to tell us when to stop propagating
    //
    context_list_a.gotoLast();
    context_list_a.setMark();
    context_list_a.gotoFirst();

    score_list_a.gotoLast();
    score_list_a.setMark();
    score_list_a.gotoFirst();
    
    // propagate all contexts in the list
    //
    boolean end_loop = context_list_a.length() < 1;
    while (!end_loop) {
    
      end_loop = (context_list_a.isMarkedElement() & score_list_a.isMarkedElement());

      // get the current context from the list
      //
      context_list_a.removeFirst(curr_context);
      score_list_a.removeFirst(curr_score);
      
      // get the last vertex of the current context
      //
      GraphVertex<SearchNode>* curr_vertex =
	curr_context->getLastVertex();
      
      // if it is NULL vertex, throw an error and exit
      //
      if (curr_vertex == (GraphVertex<SearchNode>*)NULL) {
	return Error::handle(name(), L"generateRightContexts - from NULL!", Error::ARG, __FILE__, __LINE__);
      }
      
      if (!curr_vertex->isTerm()) {
	
	// loop over arcs of the last vertex if it is not terminal vertex
	//
	for (boolean more_arcs = curr_vertex->gotoFirst();
	     more_arcs;
	     more_arcs = curr_vertex->gotoNext()) {
	  	  
	  // determine the symbol insertion penalty and transition
	  // scale factor
	  //
	  float tr_scale = getSearchLevel(level_a).getTrScale();
	  float symbol_penalty = getSearchLevel(level_a).getSymbolPenalty();
	  
	  // shift next context
	  //
	  new_score = new Float(*curr_score);	  
	  new_context = context_pool_d.shiftAndAllocate(curr_context, curr_vertex->getCurr()->getVertex());

	  // update the score with the arc score
	  //
	  long symbol_id = curr_vertex->getItem()->getSymbolId();

	  if (!getSearchLevel(level_a).isDummySymbol(symbol_id) &&
	      !getSearchLevel(level_a).isSPenaltyExcludeSymbol(symbol_id)) {
	    new_score->add((curr_vertex->getCurr()->getWeight() * tr_scale) + symbol_penalty);
	  }
	  else {
	    new_score->add(curr_vertex->getCurr()->getWeight());
	  }
	  
	  // add new context to the list
	  //
	  context_list_a.insertLast(new_context);
	  score_list_a.insertLast(new_score);
	  
	} // end of loop over all arcs of the last vertex
	
	// delete the current context from the list, since we have
	// propagated from it
	//
	delete curr_score;	

      } // end of if not terminal
      
      // if the last vertex of the current context is terminal, simply
      // shift context
      //
      else {

	curr_context = context_pool_d.shiftAndAllocate(curr_context, curr_vertex);
	
	context_list_a.insertLast(curr_context);
	score_list_a.insertLast(curr_score);	

      } // end of else terminal vertex
      
      context_list_a.gotoFirst();
      score_list_a.gotoFirst();      
      
    } // end of the loop over contexts list
    
  } // end of for depths loop

  //exit gracefully
  //
  return true;
}

// method: initializeRightContexts
//
// arguments:
//   Instance* curr_instance: (input) initial instance to start from
//   Context& init_context: (input) initial context to start from
//   float curr_weight: (input) transition probability
//   long depth: (input) right context length
//
// return: logical error status
//
// generate a list of contexts expanded from the starting context to right
// into the context depth
//
boolean HierarchicalSearch::initializeRightContexts(Instance* curr_instance_a,
				    Context& init_context_a, long level_num_a,
				    float curr_weight_a, long depth_a) {

  // declare local variables
  //
  Float* next_score = (Float*)NULL;  
  Instance* next_instance = (Instance*)NULL;    
  DoubleLinkedList<Instance> inst_list(DstrBase::USER);
  DoubleLinkedList<Float> score_list(DstrBase::USER);
 
  // set up the initial instance
  //
  if (curr_instance_a == (Instance*)NULL) {
    
    // create a new instance
    //      
    next_instance = new Instance();
    next_instance->setFrame((ulong)current_frame_d);
    
    if (search_mode_d == TRAIN) {
      next_instance->setInstanceMode(Instance::TRAIN);
    } 

    // has the history been previously generated? if so reuse the history,
    // if not generate a new history and add it to the history pool
    //
    next_instance->setHistory(history_pool_d.initAndAllocate());
    next_instance->setSymbolStack(history_pool_d.initAndAllocate());

    // initialize the structure that holds the n-symbols
    //
    initializeNsymbolInstance(next_instance);
  }
  else {
    
    // create a new instance
    //      
    next_instance = new Instance(*curr_instance_a);
    next_instance->setFrame((ulong)current_frame_d);
  }

  // output the debugging information
  //
  if (debug_level_d >= Integral::ALL) {
    init_context_a.print();
  }
    
  // has the context been previously generated? if so reuse the context,
  // if not generate a new context and add it to the context pool
  //
  next_instance->setSymbol(context_pool_d.get(init_context_a));
  
  // generate right contexts for the instance
  //
  inst_list.insertLast(next_instance);
  lookAhead(level_num_a, depth_a, inst_list, score_list);

  // generate a new instance for each right context
  //
  for (boolean more_items = (inst_list.gotoFirst() && score_list.gotoFirst());
       more_items;
       more_items = (inst_list.gotoNext() && score_list.gotoNext())) {

    // retrieve the current instance
    //
    next_score = score_list.getCurr();
    next_instance = inst_list.getCurr();
    
    // add the instance to the search node's instance list
    //
    if (!addHypothesisPath(next_instance, curr_instance_a,
			   level_num_a, *next_score)) {
      return Error::handle(name(), L"initializeRightContexts", Error::ARG, __FILE__, __LINE__);
    }
  }

  // free allocated memory
  //
  score_list.setAllocationMode(DstrBase::SYSTEM);  
  score_list.clear();
  
  // exit gracefully
  //
  return true;
}

// method: isTerminal
//
// arguments:
//   Instance* curr_instance: (input) instance in hypotheses space
//   long level_num: (input) current level number
//
// return: logical error status
//
// method determines if the instance has reached the end of the subgraph
//
boolean HierarchicalSearch::isTerminal(Instance* curr_instance_a,
				       long level_num_a) {
  
  // declare local variables
  //
  GraphVertex<SearchNode>* next_vertex = (GraphVertex<SearchNode>*)NULL;  
  GraphVertex<SearchNode>* central_vertex = (GraphVertex<SearchNode>*)NULL;
  GraphVertex<SearchNode>* skip_vertex = (GraphVertex<SearchNode>*)NULL;  

  // when the internal state of the instance is pseudo-active
  //
  if (curr_instance_a->getInstanceState() == Instance::PSEUDO_ACTIVE) {

    // when the skip vertex is not adjacent from the central vertex then the
    // current instance is at the end of the subgraph
    //
    if ((long)curr_instance_a->getSkipLevel() == level_num_a) {

      skip_vertex = (GraphVertex<SearchNode>*)curr_instance_a->getSkipSymbol();
      central_vertex = curr_instance_a->getSymbol()->getCentralVertex();
      
      if (!central_vertex->isAdjacent(skip_vertex)) {
	return true;
      }
    }

    // return the status
    //
    return false;
  }

  // retrieve the central vertex
  //
  central_vertex = curr_instance_a->getSymbol()->getCentralVertex();  
  if (central_vertex == (GraphVertex<SearchNode>*)NULL) {
    return Error::handle(name(), L"isTerminal - central vertex is null", Error::ARG, __FILE__, __LINE__);
  }

  // retrieve the vertex that will be shifted into central vertex
  //
  next_vertex = curr_instance_a->getSymbol()->getAfterCentralVertex();
  if (next_vertex == (GraphVertex<SearchNode>*)NULL) {
    return Error::handle(name(), L"isTerminal - next vertex is null", Error::ARG, __FILE__, __LINE__);
  }  

  // when the next vertex is not adjacent from the central vertex then the
  // current instance is at the end of the subgraph
  //
  if (!central_vertex->isAdjacent(next_vertex)) {
    return true;
  }
  
  // return the status
  //
  return false;
}

// method: extendRightContexts
//
// arguments:
//   Instance* curr_instance: (input) initial instance to start from
//   long level_num: (input) level number in the hierarchy
//
// return: logical error status
//
// generate a list of contexts expanded from the starting context to right
// into the context depth
//
boolean HierarchicalSearch::extendRightContexts(Instance* curr_instance_a,
						long level_num_a,
						Context* prev_symbol_a) {
  
  // declare local variables
  //
  Float* next_score = (Float*)NULL;  
  Instance* next_instance = (Instance*)NULL;  
  DoubleLinkedList<Instance> inst_list(DstrBase::USER);  
  DoubleLinkedList<Float> score_list(DstrBase::USER);
  
  // create a new instance
  //    
  next_instance = new Instance(*curr_instance_a);
  next_instance->setFrame((ulong)current_frame_d);

  // set the current symbol if it is valid
  //
  if (prev_symbol_a != (Context*)NULL) {
    next_instance->setSymbol(prev_symbol_a);
  }
  
  // generate right contexts for the instance
  //
  inst_list.insertLast(next_instance);
  lookAhead(level_num_a, 1, inst_list, score_list);
  
  // generate a new instance for each right context
  //
  for (boolean more_items = (inst_list.gotoFirst() && score_list.gotoFirst());
       more_items;
       more_items = (inst_list.gotoNext() && score_list.gotoNext())) {
    
    // retrieve the current instance
    //
    next_score = score_list.getCurr();    
    next_instance = inst_list.getCurr();

    // add the instance to the search node's instance list
    //
    if (!addHypothesisPath(next_instance, curr_instance_a,
			   level_num_a, *next_score)) {
      return Error::handle(name(), L"extendRightContexts", Error::ARG, __FILE__, __LINE__);
    }    
  }

  // free allocated memory
  //
  score_list.setAllocationMode(DstrBase::SYSTEM);  
  score_list.clear();
  
  // exit gracefully
  //
  return true;
}

// method: printHistory
//
// arguments:
//   const History* history: (input) history of the instance
//
// return: logical error status
//
boolean HierarchicalSearch::printHistory(const History* history_a) {

  // declare local variables
  //
  boolean more = false;
  
  // save the current state
  //
  const_cast<History* >(history_a)->setMark();

  // print each level of the history
  //
  for (more = const_cast<History* >(history_a)->gotoFirst();
       more;
       more = const_cast<History* >(history_a)->gotoNext()) {
    const_cast<History* >(history_a)->getCurr()->print();
  }

  // restore the saved state
  //
  const_cast<History* >(history_a)->gotoMark();
  
  // exit gracefully
  //
  return true;  
}

// method: propagateUp
//
// arguments:
//   long curr_level: (input) current level in the hierarchy
//   long level: (input) level in the hierarchy we started at
//   long curr_depth: (input) current right context length
//   long depth: (input) desired right context length
//   DoubleLinkedList<Instance>& inst_list: (output) new instances generated
//   DoubleLinkedList<History>& curr_hist: (input) working history
//   DoubleLinkedList<History>& prev_hist: (input) working history
//   DoubleLinkedList<History>& score: (output) transition probabilities
//
// return: logical error status
//
// method propagates up the search hoerarchy in the process of generating
// right context in cross-word decoding
//
boolean HierarchicalSearch::propagateUp(
				      long curr_level_a, long level_a,
				      long curr_depth_a, long depth_a,
				      DoubleLinkedList<Instance>& inst_list_a,
				      DoubleLinkedList<History>& curr_hist_a,
				      DoubleLinkedList<History>& prev_hist_a,
				      DoubleLinkedList<Float>& score_a) {
  
  // when we are at the top most level we cannot go up the hiererchy
  //
  if (curr_level_a == (long)initial_level_d) {
    return false;
  }

  // ascend to the previous level in the hierarchy and look across symbols
  //  
  else {

    // pop the current stack context and push it into the previous stack
    //
    Context* symbol = curr_hist_a.getCurr()->pop();
    prev_hist_a.getCurr()->push(symbol);
    ascend(inst_list_a.getCurr());

    // output the debugging information
    //
    if (debug_level_d >= Integral::ALL) {
      inst_list_a.getCurr()->getSymbol()->print();
    }
    
    // recursively call the lookahead helper - decrement the current level
    //
    if (!lookAheadHelper((GraphVertex<SearchNode>*)NULL,
			 curr_level_a-1, level_a,
			 curr_depth_a, depth_a,
			 inst_list_a,
			 curr_hist_a, prev_hist_a,
			 score_a)) {
      return false;
    }
  }
	  
  // return the status
  //
  return true;  
}

// method: propagateDown
//
// arguments:
//   Context* symbol: (input) initial context to start with
//   Context* symbol1: (input) working initial context
//   GraphVertex<SearchNode>* curr_vertex: (input) initial graph vertex
//   long curr_level: (input) current level in the hierarchy
//   long level: (input) level in the hierarchy we started at
//   long curr_depth: (input) current right context length
//   long depth: (input) desired right context length
//   DoubleLinkedList<Instance>& inst_list: (output) new instances generated
//   DoubleLinkedList<History>& curr_hist: (input) working history
//   DoubleLinkedList<History>& prev_hist: (input) working history
//   DoubleLinkedList<History>& score: (output) transition probabilities
//
// return: logical error status
//
// method propagates down the search hoerarchy in the process of generating
// right context in cross-word decoding
//
boolean HierarchicalSearch::propagateDown(
				      Context* symbol_a,
				      Context* symbol1_a,
				      GraphVertex<SearchNode>* curr_vertex_a,
				      long curr_level_a, long level_a,
				      long curr_depth_a, long depth_a,
				      DoubleLinkedList<Instance>& inst_list_a,
				      DoubleLinkedList<History>& curr_hist_a,
				      DoubleLinkedList<History>& prev_hist_a,
				      DoubleLinkedList<Float>& score_a) {

  // declare local variables
  //
  GraphVertex<SearchNode>* start_vertex = (GraphVertex<SearchNode>*)NULL;

  if ((symbol_a == (Context*)NULL) || (symbol1_a == (Context*)NULL)) {
    return Error::handle(name(), L"propagateDown", Error::ARG, __FILE__, __LINE__);
  }
  
  // we cannot propagate down if the current level is equal to the level
  //
  if (curr_level_a == level_a) {
    return false;
  }
  
  // generate the context to index the subgraph at the lower level
  //
  SearchLevel& sl_curr = getSearchLevel(curr_level_a);
  SearchLevel& sl_lower = getSearchLevel(curr_level_a+1);

  Context* symbol = symbol_a;
  if (!symbol->getLastVertex()->isTerm()) {
    symbol = context_pool_d.extendAndAllocate(symbol, curr_vertex_a);
  }

  // output the debugging information
  //
  if (debug_level_d >= Integral::ALL) {
    symbol->print();
  }

  Context* symbol1 = symbol1_a;
  symbol1 = context_pool_d.shiftAndAllocate(symbol1, curr_vertex_a);
  
  // shift the history at the current level
  //	
  Context* tmp_symbol = inst_list_a.getCurr()->getSymbol();
  inst_list_a.getCurr()->setSymbol(symbol);

  Context* tmp_symbol1 = curr_hist_a.getCurr()->pop();
  curr_hist_a.getCurr()->push(symbol1);
	
  // retrieve the subgraph at the lower level
  //
  Context* conv_context = (Context*)NULL;	
  symbol1->convert(conv_context);
  Ulong* subgr_ind = sl_curr.getSubGraphIndex(*conv_context);
  if (conv_context != (Context*)NULL) {
    delete conv_context;
  }
	
  // make sure we have a valid subgraph index
  //
  if (subgr_ind == (Ulong*)NULL) {

    // restore the history stach to its previous state
    //
    inst_list_a.getCurr()->setSymbol(tmp_symbol);
    
    symbol1 = curr_hist_a.getCurr()->pop();
    curr_hist_a.getCurr()->push(tmp_symbol1);
    
    return false;
  }
	
  // get the start vertex of the subgraph
  //
  start_vertex = (sl_lower.getSubGraph((long)*subgr_ind)).getStart();
	
  // insert the history at the next level
  //
  tmp_symbol1 = prev_hist_a.getCurr()->pop();
  curr_hist_a.getCurr()->push(tmp_symbol1);
  descend(inst_list_a.getCurr());
  
  // recursively call the lookahead helper - incerment the current level
  //  
  if (!lookAheadHelper(start_vertex,
		       curr_level_a+1, level_a,
		       curr_depth_a, depth_a,
		       inst_list_a,
		       curr_hist_a, prev_hist_a,
		       score_a)) {
    return false;
  }
  
  // return the status
  //
  return true;  
}

// method: lookAhead
//
// arguments:
//   long level: (input) level in the hierarchy we started at
//   long depth: (input) desired right context length
//   DoubleLinkedList<Instance>& inst_list: (output) instances generated
//   DoubleLinkedList<Float>& score: (output) transition probabilities
//
// return: logical error status
//
// method propagates up and down the search hoerarchy in the process of
// generating right context in corss-word decoding
//
boolean HierarchicalSearch::lookAhead(long level_a, long depth_a,
				      DoubleLinkedList<Instance>& inst_list_a,
				      DoubleLinkedList<Float>& score_a) {

  // declare local variables
  //
  History* init_history = (History*)NULL;
  Instance* init_instance = (Instance*)NULL;  
  GraphVertex<SearchNode>* init_vertex = (GraphVertex<SearchNode>*)NULL;
  
  DoubleLinkedList<History> curr_hist(DstrBase::USER);  
  DoubleLinkedList<History> prev_hist(DstrBase::USER);  
  
  // make sure the instance list is not empty
  //
  if (inst_list_a.isEmpty()) {
    return false;
  }

  init_instance = inst_list_a.getFirst();
  if (init_instance == (Instance*)NULL) {
    return Error::handle(name(), L"lookAhead", Error::ARG, __FILE__, __LINE__);
  }

  // make sure the symbol associated with the instance is not null
  //
  if (init_instance->getSymbol() == (Context*)NULL) {
    return Error::handle(name(), L"lookAhead", Error::ARG, __FILE__, __LINE__);
  }

  // when the internal state of the instance is pseudo-active
  //
  if ((init_instance->getInstanceState() == Instance::PSEUDO_ACTIVE)
      && (init_instance->getSkipLevel() == (ulong)level_a)) {

    // swap the contexts
    //
    ulong skip_symbol = init_instance->getSkipSymbol();
    GraphVertex<SearchNode>* tmp_vertex =
      init_instance->getSymbol()->getCentralVertex();
    
    init_instance->setSymbol(context_pool_d.setCentralAndAllocate(init_instance->getSymbol(), (GraphVertex<SearchNode>*)skip_symbol));
    init_instance->setSkipSymbol((ulong)tmp_vertex);

    // set the internal state for the instance to active
    //
    score_a.insertLast(new Float(0.0));    
    init_instance->setInstanceState(Instance::ACTIVE);

    // exit gracefully
    //  
    return true;
  }
  
  // when the internal state of the instance is active
  //
  if ((init_instance->getInstanceState() == Instance::ACTIVE)
      && (init_instance->getSkipLevel() == (ulong)level_a)) {    

    // setup the context for the instance
    //
    ulong tmp_symbol = init_instance->getSkipSymbol();
    init_instance->setSymbol(context_pool_d.setCentralAndAllocate(init_instance->getSymbol(), (GraphVertex<SearchNode>*)tmp_symbol));

    // set the internal state for the instance to inactive
    //
    init_instance->setInstanceState(Instance::INACTIVE);    
  }


  // output the debugging information
  //
  if (debug_level_d >= Integral::ALL) {

    // print the symbol
    //
    init_instance->getSymbol()->print();

    // print the history
    //
    if (init_instance->getHistory() != (History*)NULL) {
      printHistory(init_instance->getHistory());
    }

    // print the symbol stack
    //
    if (init_instance->getSymbolStack() != (History*)NULL) {
      printHistory(init_instance->getSymbolStack());
    }    
  }
  
  // get the history associated with the instance
  //
  init_history = new History(*init_instance->getHistory());
  init_history->push(init_instance->getSymbol());
  
  // initialize the lists before generating the right context
  //
  score_a.insertLast(new Float(0.0));
  curr_hist.insertLast(new History(*init_history));
  prev_hist.insertLast(new History());
  
  // initialize the vertices
  //
  init_vertex = init_instance->getSymbol()->getLastVertex();

  // generate right context for the central vertex
  //
  lookAheadHelper(init_vertex,
		  level_a, level_a,
		  0, depth_a,
		  inst_list_a,
		  curr_hist, prev_hist,
		  score_a);

  // free allocated memory
  //
  delete init_history;
  init_history = (History*)NULL;
    
  curr_hist.setAllocationMode(DstrBase::SYSTEM);
  curr_hist.clear();
  prev_hist.setAllocationMode(DstrBase::SYSTEM);
  prev_hist.clear();   
  
  // exit gracefully
  //
  return true;
}

// method: lookAheadHelper
//
// arguments:
//   GraphVertex<SearchNode>* curr_vertex: (input) initial graph vertex
//   long curr_level: (input) current level in the hierarchy
//   long level: (input) level in the hierarchy we started at
//   long curr_depth: (input) current right context length
//   long depth: (input) desired right context length
//   DoubleLinkedList<Instance>& inst_list: (output) new instances generated
//   DoubleLinkedList<History>& curr_hist: (input) working history
//   DoubleLinkedList<History>& prev_hist: (input) working history
//   DoubleLinkedList<History>& score: (output) transition probabilities
//
// return: logical error status
//
// method propagates up and down the search hoerarchy in the process of
// generating right context in corss-word decoding
//
boolean HierarchicalSearch::lookAheadHelper(
			      GraphVertex<SearchNode>* curr_vertex_a,
			      long curr_level_a, long level_a,
			      long curr_depth_a, long depth_a,
			      DoubleLinkedList<Instance>& inst_list_a,
			      DoubleLinkedList<History>& curr_hist_a,
			      DoubleLinkedList<History>& prev_hist_a,
			      DoubleLinkedList<Float>& score_a) {
    
  // declare local variables
  //  
  GraphVertex<SearchNode>* next_vertex = (GraphVertex<SearchNode>*)NULL;
  
  // when the current depth is equal to the requires depth we are done
  //
  if (curr_depth_a == depth_a) {
    return true;
  }

  // when we are at the level needed to generate right context
  //
  if (curr_level_a == level_a) {

    // when the current context has a right context length of zero and it
    // has an extension, i.e., the context has been previously extended
    //
    if (inst_list_a.getCurr()->getSymbol()->isExtended()) {

      Context* symbol = inst_list_a.getCurr()->getSymbol();
      symbol = context_pool_d.foldAndAllocate(symbol);
      inst_list_a.getCurr()->setSymbol(symbol);

      // output the debugging information
      //
      if (debug_level_d >= Integral::ALL) {
	inst_list_a.getCurr()->getSymbol()->print();
      }      
      return true;
    }
    
    // when the current vertex is a term - shift the context
    //
    if (curr_vertex_a->isTerm()) {

      Context* symbol = curr_hist_a.getCurr()->pop();
      symbol = context_pool_d.shiftAndAllocate(symbol, curr_vertex_a);

      curr_hist_a.getCurr()->push(symbol);
      inst_list_a.getCurr()->setSymbol(symbol);

      // output the debugging information
      //
      if (debug_level_d >= Integral::ALL) {
	inst_list_a.getCurr()->getSymbol()->print();	
      }      
      return true;
    }
    
    // loop over all next vertices of the current vertex
    //
    long index = 0;

    Float* curr_score = new Float(*score_a.getCurr());
    History* prev_history = new History(*prev_hist_a.getCurr());
    History* curr_history = new History(*curr_hist_a.getCurr());
    Instance* curr_instance = new Instance(*inst_list_a.getCurr());
    
    for (boolean more = curr_vertex_a->gotoFirst(); more;
	 more = curr_vertex_a->gotoNext(), index++) {
    
      // make sure each next vertex has a unique history
      //
      if (index > 0) {
	score_a.insertLast(new Float(*curr_score));	
	curr_hist_a.insertLast(new History(*curr_history));
	prev_hist_a.insertLast(new History(*prev_history));
	inst_list_a.insertLast(new Instance(*curr_instance));
      }

      // retrieve the next vertex
      //
      next_vertex = curr_vertex_a->getCurr()->getVertex();

      // determine the transition scale factor for the current level
      //	
      long symbol_id = next_vertex->getItem()->getSymbolId();	
      float tr_scale = getSearchLevel(level_a).getTrScale();
      float symbol_penalty = getSearchLevel(level_a).getSymbolPenalty();
      
      // update the transition score
      //
      float trans_score = curr_vertex_a->getCurr()->getWeight() * tr_scale;
      score_a.getCurr()->assign(*(score_a.getCurr()) + trans_score);
      
      // when the next vertex is NOT a term node - stay at the same level
      //
      if (!next_vertex->isTerm()) {

	// update the score
	//
	if (!getSearchLevel(level_a).isDummySymbol(symbol_id) &&
	    !getSearchLevel(level_a).isSPenaltyExcludeSymbol(symbol_id)) {
	  inst_list_a.getCurr()->setScore(inst_list_a.getCurr()->getScore() + symbol_penalty);
	}
	    
	// when the next vertex is a skip symbol
	//
	if (getSearchLevel(level_a).isSkipSymbol(symbol_id)) {

	  // set the internal state for the instance to pseudo-active
	  //
	  inst_list_a.getCurr()->setInstanceState(Instance::PSEUDO_ACTIVE);

	  // save the skip symbol for later retrieval
	  //
	  inst_list_a.getCurr()->setSkipLevel((ulong)level_a);	  
	  inst_list_a.getCurr()->setSkipSymbol((ulong)next_vertex);
	    
	  // recursively call the lookahead helper - incerment the depth
	  //
	  lookAheadHelper(next_vertex,
			  curr_level_a, level_a,
			  curr_depth_a, depth_a,
			  inst_list_a,
			  curr_hist_a, prev_hist_a,
			  score_a);	  
	}
	
	// when the next vertex is not a skip symbol
	//
	else {

	  // shift the right context of the symbol into the current context
	  //
	  Context* symbol = curr_hist_a.getCurr()->pop();
	  symbol = context_pool_d.shiftAndAllocate(symbol, next_vertex);
	  
	  curr_hist_a.getCurr()->push(symbol);	  
	  inst_list_a.getCurr()->setSymbol(symbol);
	  
	  // output the debugging information
	  //
	  if (debug_level_d >= Integral::ALL) {
	    symbol->print();
	  }
	  
	  // recursively call the lookahead helper - incerment the depth
	  //
	  lookAheadHelper(next_vertex,
			  curr_level_a, level_a,
			  curr_depth_a+1, depth_a,
			  inst_list_a,
			  curr_hist_a, prev_hist_a,
			  score_a);
	}
      }

      // when the next vertex is a terminal node - ascend one level
      //
      else {

	// ascend to the previous level in the hierarchy
	//
	if (!propagateUp(curr_level_a, level_a,
			 curr_depth_a, depth_a,
			 inst_list_a,
			 curr_hist_a, prev_hist_a,
			 score_a)) {

	  // restore the original contents of the current list
	  //
	  while (!prev_hist_a.getCurr()->isEmpty()) {

	    descend(inst_list_a.getCurr());
	    Context* symbol = prev_hist_a.getCurr()->pop();
	    curr_hist_a.getCurr()->push(symbol);

	    // output the debugging information
	    //
	    if (debug_level_d >= Integral::ALL) {
	      symbol->print();
	    }
	  }
	  
	  // shift !NO_RIGHT_CONTEXT for the required depth
	  //	  
	  for (int i=curr_depth_a; i<depth_a; i++) {

	    Context* symbol = curr_hist_a.getCurr()->pop();
	    symbol = context_pool_d.shiftAndAllocate(symbol, next_vertex);

	    curr_hist_a.getCurr()->push(symbol);
	    inst_list_a.getCurr()->setSymbol(symbol);
	  }

	  // free allocated memory
	  //
	  delete curr_score;
	  delete prev_history;
	  delete curr_history;
	  delete curr_instance;

	  // exit gracefully
	  //
	  return true;
	}
      }
    }

    // free allocated memory
    //
    delete curr_score;
    delete prev_history;
    delete curr_history;
    delete curr_instance;
  }

  // when we are NOT at the level needed to generate right context
  //  
  else {

    // make a copy of the current context
    //
    Context* symbol = inst_list_a.getCurr()->getSymbol();    
    Context* symbol1 = curr_hist_a.getCurr()->peek();

    // output the debugging information
    //
    if (debug_level_d >= Integral::ALL) {
      symbol->print();
    }
    
    if (curr_vertex_a == (GraphVertex<SearchNode>*)NULL) {
      curr_vertex_a = symbol->getLastVertex();
    }    

    // when the current vertex is a terminal we cannot proceed further
    //
    if (curr_vertex_a->isTerm()) {
      return false;
    }
    
    // loop over all next vertices of the current vertex
    //
    long index = 0;

    Float* curr_score = new Float(*score_a.getCurr());
    History* prev_history = new History(*prev_hist_a.getCurr());
    History* curr_history = new History(*curr_hist_a.getCurr());
    Instance* curr_instance = new Instance(*inst_list_a.getCurr());
    
    for (boolean more=curr_vertex_a->gotoFirst(); more;
	 more=curr_vertex_a->gotoNext(), index++) {
      
      // make sure each next vertex has a unique history
      //
      if (index > 0) {
	score_a.insertLast(new Float(*curr_score));
	curr_hist_a.insertLast(new History(*curr_history));
	prev_hist_a.insertLast(new History(*prev_history));
	inst_list_a.insertLast(new Instance(*curr_instance));	
      }
      
      // retrieve the next vertex
      //
      next_vertex = curr_vertex_a->getCurr()->getVertex();

      // determine the transition scale factor for the current level
      //	
      float tr_scale = getSearchLevel(level_a).getTrScale();
      
      // update the transition score
      //
      float trans_score = curr_vertex_a->getCurr()->getWeight() * tr_scale;
      score_a.getCurr()->assign(*(score_a.getCurr()) + trans_score);
      
      // when the next vertex is NOT a term node - descend one level
      //
      if (!next_vertex->isTerm()) {

	// descend to the next level in the hierarchy
	//	
	if (!propagateDown(symbol,
			   symbol1,
			   next_vertex,
			   curr_level_a, level_a,
			   curr_depth_a, depth_a,
			   inst_list_a,
			   curr_hist_a, prev_hist_a,
			   score_a)) {

	  // free allocated memory
	  //
	  delete curr_score;
	  delete prev_history;
	  delete curr_history;
	  delete curr_instance;

	  // exit gracefully
	  //
	  return false;
	}
      }
      
      // when the next vertex is a terminal node - ascend one level
      //      
      else {

	// ascend to the previous level in the hierarchy
	//
	if (!propagateUp(curr_level_a, level_a,
			 curr_depth_a, depth_a,
			 inst_list_a,
			 curr_hist_a, prev_hist_a,
			 score_a)) {

	  // retrieve the current context
	  //
	  Context* symbol = inst_list_a.getCurr()->getSymbol();
	  Context* symbol1 = curr_hist_a.getCurr()->peek();
      
	  // descend to the next level in the hierarchy
	  //
	  if (!propagateDown(symbol,
			     symbol1,
			     next_vertex,
			     curr_level_a, level_a,
			     curr_depth_a, depth_a,
			     inst_list_a,
			     curr_hist_a, prev_hist_a,
			     score_a)) {

	    // pop the previous stack context and push into the current stack
	    //
	    descend(inst_list_a.getCurr());
	    Context* symbol = prev_hist_a.getCurr()->pop();
	    curr_hist_a.getCurr()->push(symbol);

	    // free allocated memory
	    //
	    delete curr_score;
	    delete prev_history;
	    delete curr_history;
	    delete curr_instance;

	    // exit gracefully
	    //
	    return false;
	  }
	}
      }
    }

    // free allocated memory
    //
    delete curr_score;
    delete prev_history;
    delete curr_history;
    delete curr_instance;
  }
  
  // exit gracefully
  //
  return true;  
}
