// file: $isip/class/search/HierarchicalSearch/hsrch_14.cc
// version: $Id: hsrch_14.cc 9742 2004-08-20 20:43:15Z may $
//

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

// method: printInstance
//
// arguments:
//  Instance* instance_a: (input) the instance to be printed
//  long     level_num_a: (input) the search level
//  boolean  recursive_a: (input) recursive or not
//
// return: logical error status
//
// print instances
//
boolean HierarchicalSearch::printInstance(Instance* new_instance_a,
					  long level_num_a,
					  boolean recursive_a) {

  // lcoal variable
  //
  String val;
  String out;
  SearchSymbol start_sym;
  float old_score = 0.0;
  long old_frame = -1;
  SearchNode* old_node = (SearchNode*)NULL;
  Instance* old_instance = (Instance*)NULL;
  GraphVertex<SearchNode>* old_vert = (GraphVertex<SearchNode>*)NULL;
  Context* old_context = (Context*)NULL;
  boolean recursive = true;
  long center = 0;

  // print level information
  //
  if ( level_num_a >= 0 ){
    center = getSearchLevel(level_num_a).getRightContext() + 1;
  }
  
  old_instance = new_instance_a;

  while ( recursive ){

    val.assign(L"");
    
    // old instance not NULL
    //
    if (old_instance != (Instance*)NULL) {
      
      // get current instance info
      //
      old_context = old_instance->getSymbol();
      
      // get the current context length
      //
      long total_length = old_context->length();
      
      // loop over all context symbols in current context, starting from
      // the oldest one
      //
      for (long i = total_length; i > 0; i--) {
	
	// get the i-th previous context symbol
	//
	old_vert = (GraphVertex<SearchNode>*)(ulong)(*old_context)(-i);
	
	// central vertex is NULL
	//
	if ( old_vert == (GraphVertex<SearchNode>*)NULL ){
	  start_sym.assign(L"_NULL_");
	}

	// central vertex is not NULL
	//
	else {
	
	  old_node = old_vert->getItem();
	  old_frame = old_instance->getFrame();
	  old_score = old_instance->getScore();
	
	  if ( old_node == &DiGraph<SearchNode>::START_OBJ) {
	    start_sym.assign(L"_START_");
	  }
	  else if (old_node == &DiGraph<SearchNode>::TERM_OBJ) {
	    start_sym.assign(L"_TERM_");
	  }
	  else {
	    old_node->getSymbol(start_sym);
	  }
	}
	
	val.concat(start_sym);

	// put in a form of b-oy+r
	//
	if ( i > 1 && i > center ){
	  val.concat(L"-");
	}
	else if( i > 1 ){
	  val.concat(L"+");
	}
      } // end of for loop 
    } // old instance is not NULL

    // else old instance is a NULL
    //
    else{
      start_sym.assign(L"NULL_INSTANCE");
      val.concat(start_sym);
    }

    start_sym.assign(val);

    // print out the trace
    //
    out.concat(L"[");
    val.assign((long)old_frame);  
    out.concat(val);
    out.concat(L", ");
    out.concat(start_sym);
    out.concat(L", ");
    val.assign(old_score, L"%4.7f");
    out.concat(val);
    out.concat(L", ");    
    out.concat(old_instance);
    out.concat(L"]");

    // change the flag for recursive
    //
    recursive = ( recursive_a
		  && (old_instance != (Instance*)NULL) ) 
      || (old_instance == new_instance_a);

    if ( recursive ){
      if ( old_instance == new_instance_a ){
	out.concat(L"<--");
      }
    }
    else {
      
      // print level information
      //
      if ( level_num_a >= 0 ){
	out.concat(L" ==> L");
	val.assign(level_num_a);
	out.concat(val);
      }
    }
    if ( !(old_instance == new_instance_a) ){
      Console::put(out);
      out.assign(L"");
    }
    if ( old_instance != (Instance*)NULL ){
      old_instance = old_instance->getBackPointer();
    }
  }
  
  //debug_level_d = Integral::NONE;

  // exit gracefully
  // 
  return true;
}

// method: changeHistory
//
// arguments:
//  Instance*& curr_instance_a: (input) the instance that we want to change
//  boolean ascend_a: (input) ascend or descend
//  long level_num_a: (input) current level
//  GraphVertex<SearchNode>* start_vert_a : (input) lower level start vertex
//
// return: logical error status
//
// change the history of current instance
//
boolean HierarchicalSearch::changeHistory(Instance*& curr_instance_a,
					  boolean ascend_a,
					  long level_num_a,
				       GraphVertex<SearchNode>* start_vert_a){
  // if going up - ascend
  //
  if ( ascend_a ){
    
    curr_instance_a->setSymbol(context_pool_d.setLastAndAllocate(curr_instance_a->getSymbol(), (GraphVertex<SearchNode>*)NULL));
    ascend(curr_instance_a);
  }
  // if going down - descend
  //
  else {

    // if no preHistory, generate new context
    //
    if ((curr_instance_a->getSymbolStack() != (History*)NULL) &&
	curr_instance_a->getSymbolStack()->isEmpty()) {

      // set the context length
      //
      long central_pos =
	getSearchLevel(level_num_a + 1).getRightContext() + 1;
      long left_context_length =
	getSearchLevel(level_num_a + 1).getLeftContext();
      
      long total_context_length = left_context_length + central_pos;

      // error checking
      //
      if (getSearchLevel(level_num_a + 1).getLeftContext() !=
	  getSearchLevel(level_num_a + 1).getRightContext()) {
	return Error::handle(name(), L"changeHistory - left and right context lengths must be the same", Error::ARG, __FILE__, __LINE__);	
      }
	    
      // initialize left vertices of the context
      //
      Context new_context(total_context_length, central_pos);
      for (long i = 0; i < total_context_length; i++) {
	new_context.assignAndAdvance((ulong)start_vert_a);
      }

      // set current history
      //
      Context* symbol = context_pool_d.get(new_context);

      if (curr_instance_a->getSymbol() != (Context*)NULL) {
	curr_instance_a->setHistory(history_pool_d.pushAndAllocate(curr_instance_a->getHistory(), curr_instance_a->getSymbol()));
      }
      curr_instance_a->setSymbol(symbol);
    }
    
    // use preHistory
    //
    else{
      descend(curr_instance_a);
    }
  }
  
  // exit
  //
  return true;
  
}

// method: addInstance
//
// arguments:
//  long level_num_a: (input) the level for which we will add instance
//  Instance*& curr_instance_a: (input) the current instance 
//  Instance*& next_instance_a: (input) the next instance that we added
//  boolean pruning_a: (input) do a pruning or not
//
// return: logical error status
//
// add the instance to a certain level 
//
boolean HierarchicalSearch::addInstance(long level_num_a,
					Instance*& curr_instance_a,
					Instance*& next_instance_a,
					boolean pruning_a) {
  
  // local variable
  //
  float curr_score;
  GraphVertex<SearchNode>* central_vertex = next_instance_a->getSymbol()->getCentralVertex();
  
  if (central_vertex == (GraphVertex<SearchNode>*)NULL ) {
    
    // can't pruning in this case
    //
    pruning_a = false;
  }
  else if (central_vertex->isStart()) {
    
    // can't pruning in this case
    //
    pruning_a = false;
  }

  // determine the symbol insertion penalty
  //  
  if ((central_vertex != (GraphVertex<SearchNode>*)NULL) &&
      !(central_vertex->isStart()) && !(central_vertex->isTerm())) {
    
    long next_instance_level =
      central_vertex->getItem()->getSearchLevel()->getLevelIndex();
    
    float symbol_penalty =
      getSearchLevel(next_instance_level).getSymbolPenalty();
    long symbol_id = central_vertex->getItem()->getSymbolId();
    
    // add the symbol insertion penalty if applicable
    //
    if (!getSearchLevel(next_instance_level).isDummySymbol(symbol_id) &&
	!getSearchLevel(next_instance_level).isSPenaltyExcludeSymbol(symbol_id)
	&& (central_vertex->getItem() != (SearchNode*)NULL)) {
      next_instance_a->setScore(next_instance_a->getScore() + symbol_penalty);
    }
  }

  // set the backpointer
  //
  next_instance_a->setBackPointer(curr_instance_a);
  
  // apply n-symbol score
  //
  applyNSymbolScore(next_instance_a, level_num_a);

  // if not pruning
  //
  if (!pruning_a || getSearchLevel(level_num_a).useSymbolGraph()) {
    
    curr_score = next_instance_a->getScore();

    // update the maximal instance score for the beam pruning
    //
    if(getSearchLevel(level_num_a).useBeam()
       && (curr_score > max_instance_scores_d(level_num_a)) ) {
      max_instance_scores_d(level_num_a) = curr_score;
    }

    // add the instance to this level's instance list
    //
    instance_lists_d(level_num_a).insertLast(next_instance_a);

    // print debugging information
    //
    if (debug_level_d >= Integral::DETAILED) {
      
      // print the best instance
      //
      printInstance(next_instance_a, level_num_a);
    }
    
    // exit gracefully
    //
    return true;
  }
  
  // add the instance to the search node's instance list
  //
  Instance* existing_instance = (Instance*)NULL;
  SearchNode* next_node = next_instance_a->getSymbol()->
    getCentralVertex()->getItem();
 
  if (next_node->insertInstance(next_instance_a, current_frame_d,
				existing_instance)) {
	  
    // add the instance to this level's instance list
    //
    instance_lists_d(level_num_a).insertLast(next_instance_a);
    
    // print debugging information
    //
    if (debug_level_d >= Integral::DETAILED) {
      
      // print the best instance
      //
      printInstance(next_instance_a, level_num_a);
    }
  }
  
  else {
    
    // delete the instance since there is an existing instance
    // - same history
    //
    
    // print debugging information
    //
    if (debug_level_d >= Integral::DETAILED) {
      
      // print the best instance
      //
      printInstance(next_instance_a, level_num_a);
    }

    delete next_instance_a;
    return false;    
  }

  curr_score = next_instance_a->getScore();

  // update the maximal instance score for the beam pruning
  //
  if(getSearchLevel(level_num_a).useBeam()
     && (curr_score > max_instance_scores_d(level_num_a)) ) {
    max_instance_scores_d(level_num_a) = curr_score;
  }
    
  // exit gracefully
  //
  return true;
  
}

// method: ascendInstance
//
// arguments:
//  long level_num: (input) the level for which we will propagate instances
//  Instance*& curr_instance_a: (input) the instance that we want to ascend
//
// return: logical error status
//
// propagate current instance up in the hierarchy.
// only instances at the end of each subgraph are considered
//
boolean HierarchicalSearch::ascendInstance(long level_num_a,
					   Instance*& curr_instance_a) {

  // local variable
  //
  Instance* new_instance = (Instance*) NULL;
  Instance* tmp_instance = (Instance*) NULL;
  Instance* middle_instance = (Instance*) NULL;

  GraphVertex<SearchNode>* tmp_vert = (GraphVertex<SearchNode>*)NULL;
  GraphArc<SearchNode>* tmp_arc = (GraphArc<SearchNode>*)NULL;
  
  float tmp_score = 0;
  boolean pruning = true;
  boolean same_level = false;
  boolean dummy_node = false;

  // must have a history
  //
  Context* curr_context = curr_instance_a->getSymbol();

  // keep the instance for propagate down
  //
  if (curr_context->getLastVertex() == (GraphVertex<SearchNode>*)NULL) {

    // keep the old instance, now exit
    //
    return false;
  }

  // last vertext is an terminal, ready for propagate up
  //
  else if (curr_context->getLastVertex()->isTerm()) {

    // new instance
    //
    //new_instance = curr_instance_a;
    new_instance = new Instance(*curr_instance_a);
    new_instance->setFrame((ulong)current_frame_d);

    // if we are at the top level, generate a NULL right context
    //
    if ( level_num_a == 0 ){
    //new_instance->setBackPointer(curr_instance_a);

      new_instance->setSymbol(context_pool_d.setLastAndAllocate(new_instance->getSymbol(), (GraphVertex<SearchNode>*)NULL));

      // add the instance to current level's instance list
      //
      addInstance(level_num_a, curr_instance_a, new_instance, pruning);
      
      // old instance was replaced, now exit
      //
      return true;
      
    }
	
    // decrement the history of the new instance
    //
    else {

      // set back pointer
      //
      new_instance->setBackPointer(curr_instance_a);
      
      // change history, go up one level
      //
      changeHistory(new_instance, true);
      
      // this part is added for skip-symbols
      // check with the instance with non-inactive state
      // if current level is one level above the skip level
      // the central vertex needs to be swapped
      //
      if ( new_instance->getInstanceState() == Instance::PSEUDO_ACTIVE &&
	   new_instance->getSkipLevel() == (ulong) level_num_a - 1 ){
    
	// are we generating a symbol graph?
	//
	if (getSearchLevel(level_num_a - 1).useSymbolGraph()) {

	  // retrieve the current symbol
	  //
	  Context* symbol = (Context*)NULL;

	  if ((symbol = new_instance->getSymbol()) == (Context*)NULL) {
	    return Error::handle(name(), L"ascendInstance", Error::ARG, __FILE__, __LINE__);
	  }
	  
	  // add the trace to the search node's trace list
	  //
	  long index = (long)nsymbol_indices_d(level_num_a - 1);
	  long order = getSearchLevel(level_num_a - 1).getNSymbolOrder();
	  
	  Instance* existing_instance = (Instance*)NULL;
	  SearchNode* search_node = symbol->getCentralVertex()->getItem();
	  
	  if (search_node->mergeInstance(new_instance, current_frame_d,
					 existing_instance, index, order)) {
		
	    SymbolGraphNode* new_symbol_node = (SymbolGraphNode*)NULL;
	    SymbolGraphNode* old_symbol_node = (SymbolGraphNode*)NULL;
	  
	    GraphVertex<SearchNode>* nv = (GraphVertex<SearchNode>*)NULL;
	    nv = new_instance->getSymbol()->getCentralVertex();
	    long symbol_id = nv->getItem()->getSymbolId();
	    
	    if (!getSearchLevel(level_num_a - 1).isDummySymbol(symbol_id) &&
		!nv->isStart() && !nv->isTerm()) {
	      
	      SearchSymbol symbol_str;
	      nv->getItem()->getSymbol(symbol_str);
	      
	      // transfer the old symbol graph node
	      //
	      old_symbol_node = curr_instance_a->getSymbolNode();
	      
	      if (old_symbol_node == (SymbolGraphNode*)NULL) {
		old_symbol_node = symbol_graph_d.getStart();
		old_symbol_node->setFrameIndex((long)0);
		old_symbol_node->setParentGraph(&symbol_graph_d);
	      }
	      
	      // when the next instance gets added to the search node
	      //      
	      if (existing_instance == (Instance*)NULL) {
		
		// when the next instance gets added to the search node
		//      
		new_symbol_node =
		  symbol_graph_d.insertNode((ulong)current_frame_d, symbol_str);
		new_instance->setSymbolNode(new_symbol_node);
		
		if (debug_level_d >= Integral::DETAILED) {
		  
		  String new_str;
		  new_symbol_node->getSymbol(new_str);
		  
		  String output(L"\n-> creating node: ");
		  output.concat(new_symbol_node);
		  output.concat(L" [");  
		  output.concat(new_str);
		  output.concat(L"], frame: ");
		  output.concat(new_symbol_node->getFrameIndex());
		  
		  output.concat(L", associated instance: ");
		  output.concat(new_instance);
		  Console::put(output);
		}
	      }
	      
	      // when the new instance replaces the existing in the search node
	      //
	      else {
		
		new_symbol_node = existing_instance->getSymbolNode();
		new_instance->setSymbolNode(new_symbol_node);
		
		if (debug_level_d >= Integral::DETAILED) {
		  
		  String new_str;
		  new_symbol_node->getSymbol(new_str);
		  
		  String output(L"\n-> transferring node: ");
		  output.concat(new_symbol_node);
		  output.concat(L" [");  
		  output.concat(new_str);
		  output.concat(L"], frame: ");
		  output.concat(new_symbol_node->getFrameIndex());
		  
		  output.concat(L", from instance: ");
		  output.concat(existing_instance);
		  output.concat(L", to instance: ");
		  output.concat(new_instance);			
		  
		  Console::put(output);
		}		      
	      }
	      
	      // insert the old symbol graph node into the current nodes history
	      //
	      float ac_score = new_instance->getScore();
	      float lm_score = new_instance->getLMScore();
	      
	      long history_paths =
		  getSearchLevel(level_num_a - 1).getHistoryPaths();
	      
	      new_symbol_node->insertPrevNode(old_symbol_node, ac_score,
					      history_paths, lm_score);
	    }
	  }
	}
	
	Instance* tmp_instance = new Instance(*new_instance);
	tmp_instance->setFrame((ulong)current_frame_d);
	//tmp_instance->setBackPointer(new_instance);
	
	// set to normal condition
	//
	tmp_instance->setInstanceState(Instance::ACTIVE);
	
	// swap the central vertex
	//
	tmp_vert = (GraphVertex<SearchNode>*)tmp_instance->getSkipSymbol();
	ulong symbol_id = (ulong)tmp_instance->getSymbol()->getCentralVertex();
	tmp_instance->setSkipSymbol(symbol_id);

	tmp_instance->setSymbol(context_pool_d.setCentralAndAllocate(tmp_instance->getSymbol(), tmp_vert));
	
	// set the one before central context
	//
	tmp_instance->getSymbol()->setBeforeCentralVertex(symbol_id);

	// print debugging information
	//
	if (debug_level_d >= Integral::DETAILED) {
	  
	  // print the best instance
	  //
	  printInstance(new_instance, level_num_a);
	}
	
	// add the instance to current level's instance list
	//
	addInstance(level_num_a - 1, new_instance, tmp_instance, pruning);

	// clean the memory
	//
	if (new_instance->getRefCount() < 1
	    && new_instance != curr_instance_a) {
	  delete new_instance;
	}

	// exit gracefully
	//
	return true;
      }
      
      if ( new_instance->getInstanceState() == Instance::ACTIVE &&
	   new_instance->getSkipLevel() == (ulong) level_num_a - 1 ){
	
	Instance* tmp_instance = new Instance(*new_instance);
	tmp_instance->setFrame((ulong)current_frame_d);
	tmp_instance->setBackPointer(new_instance);

	// set the new pointers
	//
	middle_instance = new_instance;
	new_instance = tmp_instance;
	
	// swap the central vertex
	//
	tmp_vert = (GraphVertex<SearchNode>*)new_instance->getSkipSymbol();
	ulong symbol_id = (ulong)new_instance->getSymbol()->getCentralVertex();
	new_instance->setSkipSymbol(symbol_id);

	new_instance->setSymbol(context_pool_d.setCentralAndAllocate(new_instance->getSymbol(), tmp_vert));
	
	// set to normal condition
	//
	new_instance->setInstanceState(Instance::INACTIVE);	
      }
      
      tmp_vert = new_instance->getSymbol()->getLastVertex();      
    }
  }

  // central vertext is an terminal, ready for propagate at the same level
  //
  else if (curr_context->getLastVertex()->getItem()->isDummyNode()) {

    // new instance
    //
    new_instance = curr_instance_a;
    tmp_vert = curr_context->getLastVertex();
    dummy_node = true;
    same_level = true;
  }

  // central vertext is an terminal, ready for propagate at the same level
  //
  else if (curr_context->getCentralVertex()->isStart()) {

    // new instance
    //
    new_instance = curr_instance_a;
    tmp_vert = curr_context->getLastVertex();
    same_level = true;
  }

  // last vertext is at one level above, ready for propagate up
  //
  else if (curr_context->getLastVertex()->getItem()->
	   getSearchLevel()->getLevelIndex() ==  level_num_a - 1) {

    // new instance
    //
    new_instance = new Instance(*curr_instance_a);
    new_instance->setFrame((ulong)current_frame_d);
    new_instance->setBackPointer(curr_instance_a);

    //tmp_score = getPosteriorScore(new_instance);
    //new_instance->setScore(new_instance->getScore() + tmp_score);

    // decrement the history of the new instance
    //
    changeHistory(new_instance, true);

    // add the symbol penalty while going to higher level
    //
    long new_instance_level =
      curr_context->getLastVertex()->getItem()->getSearchLevel()->getLevelIndex();
    
    float symbol_penalty =
      getSearchLevel(new_instance_level).getSymbolPenalty();
    long symbol_id = curr_context->getLastVertex()->getItem()->getSymbolId();
    
    // add the symbol insertion penalty if applicable
    //
    if (!getSearchLevel(new_instance_level).isDummySymbol(symbol_id) &&
	!getSearchLevel(new_instance_level).isSPenaltyExcludeSymbol(symbol_id)
	&& (curr_context->getLastVertex()->getItem() != (SearchNode*)NULL)) {
      new_instance->setScore(new_instance->getScore() + symbol_penalty);
    }

    new_instance->setSymbol(context_pool_d.shiftAndAllocate(new_instance->getSymbol(), curr_context->getLastVertex()));
    
    tmp_vert = new_instance->getSymbol()->getLastVertex();

    // apply n-symbol score
    //
    applyNSymbolScore(new_instance, level_num_a - 1);
  }
  
  // ready for propagate down, keep this instance
  //
  else if ( curr_context->getLastVertex()->getItem()->
	    getSearchLevel()->getLevelIndex() >=  level_num_a ) {
    
    // ready for propagate down, keep the instance
    //
    return false;
  }
  // give error message, it can not happen in a correct situation
  //
  else {
    return Error::handle(name(),
			 L"ascendInstance: Wrong instance",
			 Error::ARG,
			 __FILE__, __LINE__);
  }

  // keep the instance for propagate down
  //
  if ( tmp_vert == (GraphVertex<SearchNode>*)NULL) {
    
    // are we generating a symbol graph?
    //
    if (!same_level && getSearchLevel(level_num_a - 1).useSymbolGraph()) {

      // retrieve the current symbol
      //
      Context* symbol = (Context*)NULL;
      
      if ((symbol = new_instance->getSymbol()) == (Context*)NULL) {
	return Error::handle(name(), L"ascendInstance", Error::ARG, __FILE__, __LINE__);
      }

      // add the trace to the search node's trace list
      //
      long index = (long)nsymbol_indices_d(level_num_a - 1);
      long order = getSearchLevel(level_num_a - 1).getNSymbolOrder();

      Instance* existing_instance = (Instance*)NULL;
      SearchNode* search_node = symbol->getCentralVertex()->getItem();
      
      if (search_node->mergeInstance(new_instance, current_frame_d,
				     existing_instance, index, order)) {
	
	SymbolGraphNode* new_symbol_node = (SymbolGraphNode*)NULL;
	SymbolGraphNode* old_symbol_node = (SymbolGraphNode*)NULL;
      
	GraphVertex<SearchNode>* nv = (GraphVertex<SearchNode>*)NULL;
	nv = new_instance->getSymbol()->getCentralVertex();
	long symbol_id = nv->getItem()->getSymbolId();
	
	if (!getSearchLevel(level_num_a - 1).isDummySymbol(symbol_id) &&
	    !nv->isStart() && !nv->isTerm()) {
	    
	  SearchSymbol symbol_str;
	  nv->getItem()->getSymbol(symbol_str);
	  
	  // transfer the old symbol graph node
	  //
	  old_symbol_node = curr_instance_a->getSymbolNode();
	    
	  if (old_symbol_node == (SymbolGraphNode*)NULL) {
	    old_symbol_node = symbol_graph_d.getStart();
	    old_symbol_node->setFrameIndex((long)0);
	    old_symbol_node->setParentGraph(&symbol_graph_d);
	  }
	  		    
	  // when the next instance gets added to the search node
	  //      
	  if (existing_instance == (Instance*)NULL) {
		  
	    // when the next instance gets added to the search node
	    //      
	    new_symbol_node =
	      symbol_graph_d.insertNode((ulong)current_frame_d, symbol_str);
	    new_instance->setSymbolNode(new_symbol_node);
	    
	    if (debug_level_d >= Integral::DETAILED) {
	      
	      String new_str;
	      new_symbol_node->getSymbol(new_str);
	      
	      String output(L"\n-> creating node: ");
	      output.concat(new_symbol_node);
	      output.concat(L" [");  
	      output.concat(new_str);
	      output.concat(L"], frame: ");
	      output.concat(new_symbol_node->getFrameIndex());
	      
	      output.concat(L", associated instance: ");
	      output.concat(new_instance);
	      Console::put(output);
	    }
	  }
	  
	  // when the new instance replaces the existing in the search node
	  //
	  else {
	    
	    new_symbol_node = existing_instance->getSymbolNode();
	    new_instance->setSymbolNode(new_symbol_node);
	      
	    if (debug_level_d >= Integral::DETAILED) {
		
	      String new_str;
	      new_symbol_node->getSymbol(new_str);
	      
	      String output(L"\n-> transferring node: ");
	      output.concat(new_symbol_node);
	      output.concat(L" [");  
	      output.concat(new_str);
	      output.concat(L"], frame: ");
	      output.concat(new_symbol_node->getFrameIndex());
	      
	      output.concat(L", from instance: ");
	      output.concat(existing_instance);
	      output.concat(L", to instance: ");
	      output.concat(new_instance);			
	      
	      Console::put(output);
	    }		      
	  }
	  
	  // insert the old symbol graph node into the current nodes history
	  //
	  float ac_score = new_instance->getScore();
	  float lm_score = new_instance->getLMScore();
	  
	  long history_paths =
	    getSearchLevel(level_num_a - 1).getHistoryPaths();
	  
	  new_symbol_node->insertPrevNode(old_symbol_node, ac_score,
					  history_paths, lm_score);
	}
      }
    }
    
    // new instance
    //
    tmp_instance = new Instance(*new_instance);
    tmp_instance->setFrame((ulong)current_frame_d);

    // generate a NULL right context
    //
    tmp_instance->setSymbol(context_pool_d.shiftAndAllocate(tmp_instance->getSymbol(), (GraphVertex<SearchNode>*)NULL));

    if ( same_level ){

      // print debugging information
      //
      if (debug_level_d >= Integral::DETAILED) {
	
	// print the best instance
	//
	printInstance(new_instance, level_num_a);
      }
      
      // add the instance to current level's instance list
      //
      addInstance(level_num_a, new_instance, tmp_instance, pruning);
    }
    else {

      // print debugging information
      //
      if (debug_level_d >= Integral::DETAILED) {
	
	// print the best instance
	//
	printInstance(new_instance, level_num_a);
      }
      
      // add the instance to the higher level's instance list
      //
      addInstance(level_num_a - 1 , new_instance, tmp_instance, pruning);
    }

    // clean the memory, must clear new_instance first
    //
    if (new_instance->getRefCount() < 1
	&& new_instance != curr_instance_a) {
      delete new_instance;
    }

    // clean the memory, clear the middle_instance the second
    //
    if ( middle_instance != NULL ){
      if (middle_instance->getRefCount() < 1
	  && middle_instance != curr_instance_a) {
	delete middle_instance;
      }
    }
    
    // exit
    //
    return true;
  }
  
  // error, ISIP_REMOVE, this check can be removed
  //
  if (tmp_vert->isTerm()) {

    return Error::handle(name(),
			 L"ascendInstance: Wrong instance",
			 Error::ARG,
			 __FILE__, __LINE__);
  }
    
  // are we generating a symbol graph?
  //
  if (!same_level && !tmp_vert->isEmpty()
      && getSearchLevel(level_num_a - 1).useSymbolGraph()) {
    
    // retrieve the current symbol
    //
    Context* symbol = (Context*)NULL;
    
    if ((symbol = new_instance->getSymbol()) == (Context*)NULL) {
      return Error::handle(name(), L"ascendInstance", Error::ARG, __FILE__, __LINE__);
    }
    
    // add the trace to the search node's trace list
    //
    long index = (long)nsymbol_indices_d(level_num_a - 1);
    long order = getSearchLevel(level_num_a - 1).getNSymbolOrder();
    
    Instance* existing_instance = (Instance*)NULL;
    SearchNode* search_node = symbol->getCentralVertex()->getItem();
    
    if (search_node->mergeInstance(new_instance, current_frame_d,
				   existing_instance, index, order)) {
      
      SymbolGraphNode* new_symbol_node = (SymbolGraphNode*)NULL;
      SymbolGraphNode* old_symbol_node = (SymbolGraphNode*)NULL;
      
      GraphVertex<SearchNode>* nv = (GraphVertex<SearchNode>*)NULL;
      nv = new_instance->getSymbol()->getCentralVertex();
      long symbol_id = nv->getItem()->getSymbolId();
      
      if (!getSearchLevel(level_num_a - 1).isDummySymbol(symbol_id) &&
	  !nv->isStart() && !nv->isTerm()) {
	
	SearchSymbol symbol_str;
	nv->getItem()->getSymbol(symbol_str);
	
	// transfer the old symbol graph node
	//
	old_symbol_node = curr_instance_a->getSymbolNode();
	
	if (old_symbol_node == (SymbolGraphNode*)NULL) {
	  old_symbol_node = symbol_graph_d.getStart();
	  old_symbol_node->setFrameIndex((long)0);
	  old_symbol_node->setParentGraph(&symbol_graph_d);
	}
	
	// when the next instance gets added to the search node
	//      
	if (existing_instance == (Instance*)NULL) {
	  
	  // when the next instance gets added to the search node
	  //      
	  new_symbol_node =
	    symbol_graph_d.insertNode((ulong)current_frame_d, symbol_str);
	  new_instance->setSymbolNode(new_symbol_node);
	  
	  if (debug_level_d >= Integral::DETAILED) {
	      
	    String new_str;
	    new_symbol_node->getSymbol(new_str);
	    
	    String output(L"\n-> creating node: ");
	    output.concat(new_symbol_node);
	    output.concat(L" [");  
	    output.concat(new_str);
	    output.concat(L"], frame: ");
	    output.concat(new_symbol_node->getFrameIndex());
	    
	    output.concat(L", associated instance: ");
	    output.concat(new_instance);
	    Console::put(output);
	  }
	}
	
	// when the new instance replaces the existing in the search node
	//
	else {
	  
	  new_symbol_node = existing_instance->getSymbolNode();
	  new_instance->setSymbolNode(new_symbol_node);
	  
	  if (debug_level_d >= Integral::DETAILED) {
	    
	    String new_str;
	    new_symbol_node->getSymbol(new_str);
	    
	    String output(L"\n-> transferring node: ");
	    output.concat(new_symbol_node);
	    output.concat(L" [");  
	    output.concat(new_str);
	    output.concat(L"], frame: ");
	    output.concat(new_symbol_node->getFrameIndex());
	    
	    output.concat(L", from instance: ");
	    output.concat(existing_instance);
	    output.concat(L", to instance: ");
	    output.concat(new_instance);			
	    
	    Console::put(output);
	  }		      
	}
	
	// insert the old symbol graph node into the current nodes history
	//
	float ac_score = new_instance->getScore();
	float lm_score = new_instance->getLMScore();
	
	long history_paths =
	  getSearchLevel(level_num_a - 1).getHistoryPaths();
	
	new_symbol_node->insertPrevNode(old_symbol_node, ac_score,
					history_paths, lm_score);
      }
    }
  }
  
  // generate instances to all next vertices
  //	  
  for (boolean more_paths = tmp_vert->gotoFirst(); more_paths;
       more_paths = tmp_vert->gotoNext()) {
    
    // create a new instance, we store the extra instance so we
    // can later view the scores as they are added. this can
    // be changed later for efficiency
    //
    tmp_instance = new Instance(*new_instance);
    tmp_instance->setFrame((ulong)current_frame_d);
      
    // update the score with the arc score
    //
    tmp_arc = tmp_vert->getCurr();
    tmp_score = tmp_instance->getScore() + tmp_arc->getWeight();
    tmp_instance->setScore(tmp_score);
    
    // change the history
    //
    if ( dummy_node ){
      tmp_instance->setSymbol(context_pool_d.setLastAndAllocate(tmp_instance->getSymbol(), tmp_arc->getVertex()));
    }
    else{
      tmp_instance->setSymbol(context_pool_d.shiftAndAllocate(tmp_instance->getSymbol(), tmp_arc->getVertex()));
    }
    
    // add the instance to current level's instance list
    //
    if ( same_level ){

      // print debugging information
      //
      if (debug_level_d >= Integral::DETAILED) {
	
	// print the best instance
	//
	printInstance(new_instance, level_num_a);
      }

      // set the language model probability
      //
      if (getSearchLevel(level_num_a).useRescore()) {
	tmp_instance->setLMScore(tmp_arc->getLanguageWeight());
      }
    
      addInstance(level_num_a, new_instance, tmp_instance,  pruning);
    }
    // add the instance to the higher level's instance list
    //
    else {

      // print debugging information
      //
      if (debug_level_d >= Integral::DETAILED) {
	
	// print the best instance
	//
	printInstance(new_instance, level_num_a);
      }

      // set the language model probability
      //
      if (getSearchLevel(level_num_a - 1).useRescore()) {
	tmp_instance->setLMScore(tmp_arc->getLanguageWeight());
      }
      
      addInstance(level_num_a - 1 , new_instance, tmp_instance, pruning);
    }
  }
  
  // clean the memory, must clear new_instance first
  //
  if (new_instance->getRefCount() < 1
      && new_instance != curr_instance_a) {
    delete new_instance;
  }
  
  // clean the memory, clear the middle_instance the second
  //
  if ( middle_instance != NULL ){
    if (middle_instance->getRefCount() < 1
	&& middle_instance != curr_instance_a) {
      delete middle_instance;
    }
  }
  
  // exit gracefully
  //
  return true;
  
}

// method: descendInstance
//
// arguments:
//  long level_num: (input) the level for which we will propagate instances
//  Instance*& curr_instance_a: (input) the instance that we want to descend
//
// return: logical error status
//
// propagate the instance down the hierarchy.
//
boolean HierarchicalSearch::descendInstance(long level_num_a,
				 Instance*& curr_instance_a) {

  // local variable
  //
  Context* curr_context = curr_instance_a->getSymbol();
  Instance* new_instance = (Instance*) NULL;
  Instance* tmp_instance = (Instance*) NULL;
  
  GraphVertex<SearchNode>* tmp_vert = (GraphVertex<SearchNode>*)NULL;
  GraphArc<SearchNode>* tmp_arc = (GraphArc<SearchNode>*)NULL;
  
  float tmp_score = 0;
  boolean pruning = true;
  boolean ascend = false;

  // if central is NULL, propagate down
  //
  if (curr_context->getCentralVertex() == (GraphVertex<SearchNode>*)NULL) {

    // new instances
    //
    new_instance = new Instance(*curr_instance_a);
    new_instance->setFrame((ulong)current_frame_d);

    // descend one level
    //
    changeHistory(new_instance, ascend, level_num_a,
		  (GraphVertex<SearchNode>*)NULL);
  
    new_instance->setSymbol(context_pool_d.setLastAndAllocate(new_instance->getSymbol(), (GraphVertex<SearchNode>*)NULL));

    // add the instance to the lower level's instance list
    //
    addInstance(level_num_a + 1 , curr_instance_a, new_instance, pruning);

    // new instance is added, now exit
    //
    return true;
  }
  
  // 1) central is start, or
  // 2) last node is the dummy node, the term node or at a higher level,
  //    ready for propagate up
  if  ((curr_context->getLastVertex()
	!= (GraphVertex<SearchNode>*)NULL) &&
       ( curr_context->getCentralVertex()->isStart() ||
	 curr_context->getLastVertex()->isTerm() ||
	 curr_context->getLastVertex()->getItem()->isDummyNode() ||
	 curr_context->getLastVertex()->getItem()->
	 getSearchLevel()->getLevelIndex() <  level_num_a )) {

    // no new instance generated
    //
    return false;
    
  }

  // this part is added for skip-symbols
  // if curr_instance_a is inactive and the last vertex is "sp"
  // and level_num_a is the same as the curr_context->getLastVertex()
  // ->getItem()->getSearchLevel()->getLevelIndex()
  // 1) find all the successors of sp
  // 2) generate new instances point to the central vertex, set the status
  //    to pesudo_active, save the vertex "sp" in the instance
  // 3) return
  //
  if ( curr_context->getLastVertex() != (GraphVertex<SearchNode>*)NULL) {
    tmp_vert = curr_context->getLastVertex();
    long symbol_id = tmp_vert->getItem()->getSymbolId();

    // error checking
    //
    if ( getSearchLevel(level_num_a).isSkipSymbol(symbol_id) &&
	 getSearchLevel(level_num_a).getRightContext() > 1 ){
      return Error::handle(name(), L"propagateInstancesDown: skip symbol can't specified at the search level with right context great than one", Error::ARG,__FILE__, __LINE__);	    
    }

    // if current symbol is skip symbol
    //
    if ( getSearchLevel(level_num_a).isSkipSymbol(symbol_id) &&
	 getSearchLevel(level_num_a).getRightContext() > 0 &&
	 tmp_vert->getItem()->getSearchLevel()->getLevelIndex() == level_num_a 
         && curr_context->getCentralVertex()->getItem()->getSearchLevel()->getLevelIndex() == level_num_a ){

      // loop over all paths in the subgraph and create new
      // instances in the next level
      //
      for (boolean more_paths = tmp_vert->gotoFirst(); more_paths;
	   more_paths = tmp_vert->gotoNext()) {
      
	// initialize central vertex of the context
	//
	tmp_arc = tmp_vert->getCurr();
      
	// create a new instance, we store the extra instance so we
	// can later view the scores as they are added. this can
	// be changed later for efficiency
	//
	tmp_instance = new Instance(*curr_instance_a);
	tmp_instance->setFrame((ulong)current_frame_d);
      
	// update the score with the arc score
	//
	tmp_score = tmp_instance->getScore() + tmp_arc->getWeight();
	tmp_instance->setScore(tmp_score);
      
	// set the instance as special
	//
	tmp_instance->setSkipSymbol((ulong)tmp_vert);
	tmp_instance->setSkipLevel(level_num_a);
	tmp_instance->setInstanceState(Instance::PSEUDO_ACTIVE);
	
	tmp_instance->setSymbol(context_pool_d.setLastAndAllocate(tmp_instance->getSymbol(), tmp_arc->getVertex()));

	// set the language model probability
	//
	if (getSearchLevel(level_num_a).useRescore()) {
	  tmp_instance->setLMScore(tmp_arc->getLanguageWeight());
	}
	
	// add the instance to current level's instance list
	//
	addInstance(level_num_a, curr_instance_a, tmp_instance, false);
      }

      // exit gracefully
      //
      return true;
    }
  }
  
  // central vertext is at the same level , ready for propagate down
  //
  if (curr_context->getCentralVertex()->getItem()->
	   getSearchLevel()->getLevelIndex() ==  level_num_a ) {
    
    // new instance
    //
    new_instance = curr_instance_a;
    new_instance->setFrame((ulong)current_frame_d);

    // acces the subgraph's start vertex
    //
    SearchLevel& sl_curr = getSearchLevel(level_num_a);
    SearchLevel& sl_lower = getSearchLevel(level_num_a + 1);

    // convert context vertices to search symbol indices
    // we need to do it before hashing in getSubGraphIndex
    //
    Context convert_context = *curr_context;
    Context* tmp_context = (Context*)NULL;
    Ulong* subgr_ind = (Ulong*)NULL;
    
    long symbol_id = curr_context->getCentralVertex()->
      getItem()->getSymbolId();

    // this part is added for skip-symbols
    // if curr_instance is active and the central is sp, evaluate
    // sp and replace the central sp with the original vertex, set to 
    //
    if ( getSearchLevel(level_num_a).isSkipSymbol(symbol_id) ){
      
      // if the instance is not active, return error
      //
      if ( new_instance->getInstanceState() != Instance::ACTIVE ){
	return Error::handle(name(), L"propagateInstancesDown: search error, incorrect state for skip symbol",
			   Error::ARG,__FILE__, __LINE__);	    	
      }
      else{

	// swap the central vertex
	//
	//tmp_vert = (GraphVertex<SearchNode>*)new_instance->getSkipSymbol();
	//new_instance->getHistory()->peek()->setCentralVertex((ulong)tmp_vert);

	// back to normal operation
	//
	//new_instance->setInstanceState(Instance::INACTIVE);

      }
    }
    
    // special case for those context-less symbols
    //
    if ( getSearchLevel(level_num_a).isContextLessSymbol(symbol_id) ||
	 getSearchLevel(level_num_a).isSkipSymbol(symbol_id) ){
      
      // generate the new context
      //
      long left_context_length =
	getSearchLevel(level_num_a).getLeftContext();
      long right_context_length =
	getSearchLevel(level_num_a).getRightContext();

      long central_pos = right_context_length + 1;
      long total_context_length = left_context_length + central_pos;

      // error checking
      //
      if (getSearchLevel(level_num_a).getLeftContext() !=
	  getSearchLevel(level_num_a).getRightContext()) {
	return Error::handle(name(), L"descendInstance - left and right context lengths must be the same", Error::ARG, __FILE__, __LINE__);	
      }
      
      // initialize the left context
      //
      tmp_context = new Context(total_context_length, central_pos);

      for (long i = 0; i < left_context_length; i++) {
	tmp_context->assignAndAdvance(getSearchLevel(level_num_a).getSymbolIndex(SearchSymbol::NO_LEFT_CONTEXT));
      }

      // initialize central vertex of the context
      //
      tmp_context->assignAndAdvance(symbol_id);
      
      // initialize right vertices of the context
      //	      
      for (long i = 0; i < right_context_length; i++) {
	tmp_context->assignAndAdvance(getSearchLevel(level_num_a).getSymbolIndex(SearchSymbol::NO_RIGHT_CONTEXT));
      }
    }
    // if it is not the context less symbol
    //
    else{
      
      convert_context.convert(tmp_context);
    }

    // get the index corresponding to the current context
    //
    subgr_ind = sl_curr.getSubGraphIndex(*tmp_context);
    
    if (subgr_ind == (Ulong*)NULL) {
      String context;
      curr_context->print(context);
      String output(L"**** no subgraph found for context ");
      output.concat(context);
      output.concat(L" at level ");
      output.concat(level_num_a);
      output.concat(L" ****");
      Console::put(output);
      return Error::handle(name(), L"propagateInstancesDown", Error::ARG, __FILE__, __LINE__);	    
    }

    if (tmp_context != (Context*)NULL) {
      delete tmp_context;
    }
      
    // get the subgraph start vertex
    // 
    tmp_vert = (sl_lower.getSubGraph((long)*subgr_ind)).getStart();
        
    // loop over all paths in the subgraph and create new
    // instances in the next level
    //
    for (boolean more_paths = tmp_vert->gotoFirst(); more_paths;
	 more_paths = tmp_vert->gotoNext()) {
      
      // initialize central vertex of the context
      //
      tmp_arc = tmp_vert->getCurr();
      
      // create a new instance, we store the extra instance so we
      // can later view the scores as they are added. this can
      // be changed later for efficiency
      //
      tmp_instance = new Instance(*new_instance);
      tmp_instance->setFrame((ulong)current_frame_d);
      
      // update the score with the arc score
      //
      tmp_score = tmp_instance->getScore() + tmp_arc->getWeight();
      tmp_instance->setScore(tmp_score);
      
      // change the history
      //
      changeHistory(tmp_instance, ascend, level_num_a,
		    tmp_vert);

      tmp_instance->setSymbol(context_pool_d.setLastAndAllocate(tmp_instance->getSymbol(), tmp_arc->getVertex()));


      // print debugging information
      //
      if (debug_level_d >= Integral::DETAILED) {
	
	// print the best instance
	//
	printInstance(new_instance, level_num_a);
      }

      // set the language model probability
      //
      if (getSearchLevel(level_num_a + 1).useRescore()) {
	tmp_instance->setLMScore(tmp_arc->getLanguageWeight());
      }
      
      // add the instance to current level's instance list
      //
      addInstance(level_num_a + 1 , new_instance, tmp_instance, pruning);
    }
    
    // clean the memory
    //
    if (new_instance->getRefCount() < 1
	&& new_instance != curr_instance_a) {
      delete new_instance;
    }

    // new instance generated
    //
    return true;
  }
  // central vertext is at a lower level , ready for propagate down
  // set the nxt level context as start-start+curr_central_context
  // the start will be treated in the propagte up stage
  //
  else if (curr_context->getCentralVertex()->getItem()->
	   getSearchLevel()->getLevelIndex() >  level_num_a ) {

    // get the central vertex
    //
    tmp_vert = curr_context->getCentralVertex();
    
    // new instance
    //
    new_instance = new Instance(*curr_instance_a);
    new_instance->setFrame((ulong)current_frame_d);

    // currently , different level has different left context node
    // we must set it to the correct level
    //
    tmp_vert = tmp_vert->getItem()->getSearchLevel()->
      getSubGraph((long)0).getStart();

    // change the history
    //
    changeHistory(new_instance, ascend, level_num_a,
		  tmp_vert);

    new_instance->setSymbol(context_pool_d.setLastAndAllocate(new_instance->getSymbol(), curr_context->getCentralVertex()));
    
    // set the language model probability
    //
    if (getSearchLevel(level_num_a + 1).useRescore()) {
      new_instance->setLMScore(tmp_arc->getLanguageWeight());
    }
    
    // add the instance to current level's instance list
    //
    addInstance(level_num_a + 1 , curr_instance_a, new_instance, pruning);
    
    // new instance generated
    //
    return true;
  }

  // error, can not happen
  //
  else {
    return Error::handle(name(),
			 L"ascendInstance: Wrong instance",
			 Error::ARG,
			 __FILE__, __LINE__);
  }

  // exit gracefully
  //
  return true;
  
}

// method: propagateLexInstancesUp
//
// arguments:
//  long level_num: (input) the level for which we will propagate instances
//
// return: logical error status
//
// propagate all instances up the hierarchy. only instances at the end of a
// each subgraph are considered
//
boolean HierarchicalSearch::propagateLexInstancesUp(long level_num_a) {

  // algorithm for this method: propagateLexInstancesUp

  // define local variables
  //
  boolean status = false;
  boolean end_loop = false;
  Instance* curr_instance = (Instance*)NULL;
  Context useless;
  
  // make sure there is at least one instance to propagate
  //
  if (instance_lists_d(level_num_a).length() < 1) {
    return false;
  }
  
  // loop over all current instances and create the next level's instances.
  // all new instances will go at the end of the list so we set the mark to
  // tell us when to stop propagating
  //
  instance_lists_d(level_num_a).gotoLast();
  instance_lists_d(level_num_a).setMark();
  instance_lists_d(level_num_a).gotoFirst();
  
  while (!end_loop) {
    
    // loop until we reached the marked element
    //
    end_loop = instance_lists_d(level_num_a).isMarkedElement();
    
    // get the current first instance from the list
    //
    instance_lists_d(level_num_a).removeFirst(curr_instance);
    
    // if the instance is inactive then delete it and its backpath if necessary
    //
    if (!curr_instance->isActive()) {

      if (search_mode_d == DECODE) {
	Instance::deleteInstance(curr_instance, true);
      }
    }
    
    // else, extend paths and update scores
    //
    else {

      // if we propagate new instances, but not sure they are pruned or not
      //
      if ( ascendInstance(level_num_a, curr_instance) ) {
	
	// if all new instances are pruned
	// bump the current instance off of the instance list
	//
	if (curr_instance->getRefCount() < 1) {
	    
	  // delete the instance
	  //
	  if (search_mode_d == DECODE) {
	    Instance::deleteInstance(curr_instance, true);
	  }
	}
	else {
	  
	  // now, there is at least one new instance survived
	  //
	  status = true;
	}
	instance_lists_d(level_num_a).gotoFirst();
      }
      
      // when the instance is NOT at the end of the current subgraph we keep
      // it at this level for now it gets propagated forwards during the
      // evaluateLexInstanceModels function
      //
      else {
	
	float curr_score = curr_instance->getScore();

	// update the maximal instance score for the beam pruning
	//
	if(getSearchLevel(level_num_a).useBeam()
	  && (curr_score > max_instance_scores_d(level_num_a)) ) {
	  max_instance_scores_d(level_num_a) = curr_score;
	}
      
	// keep the instance on this level
	//
	instance_lists_d(level_num_a).insertLast(curr_instance);
	instance_lists_d(level_num_a).gotoFirst();
      }
    }
  }
  
  // exit gracefully
  //
  return status;  
}

// method: propagateLexInstancesDown
//
// arguments:
//  long level_num: (input) the level for which we will propagate LexInstances
//
// return: logical error status
//
// propagate all instances down the hierarchy. 
//
boolean HierarchicalSearch::propagateLexInstancesDown(long level_num_a) {

  // algorithm for this method:

  // define local variables
  //
  Instance* curr_instance = (Instance*)NULL;
  boolean status = false;
  boolean end_loop = false;
 
  // make sure there is at least one instance to propagate
  //
  if (instance_lists_d(level_num_a).length() < 1) {
    return false;
  }

  // switch on whether we are at the lowest level or not
  //
  // if we are not at the lowest level, push instances down the hierarchy -
  // there should be no instances left at this level when we are done
  //
  instance_lists_d(level_num_a).gotoLast();
  instance_lists_d(level_num_a).setMark();
  instance_lists_d(level_num_a).gotoFirst();
  
  if (level_num_a != getNumLevels() - 1) {
    
    // loop over all current instances and create the next level's instances
    //
    while (!end_loop) {
      
      // loop until we reached the marked element
      //
      end_loop = instance_lists_d(level_num_a).isMarkedElement();
    
      // get the current vertex in the search graph
      //
      instance_lists_d(level_num_a).removeFirst(curr_instance);

      // delete this instance if it is now inactive
      //
      if (!curr_instance->isActive()) {

	// bump the current instance off of the instance list and delete it
	// and its backpath if necessary
	//
	if (search_mode_d == DECODE) {
	  Instance::deleteInstance(curr_instance, true);
	}
      }

      // if it is active then propagate it
      //
      else {

	// if central is NULL, propagate down
	//
	if (curr_instance->getSymbol()->getCentralVertex()
	    == (GraphVertex<SearchNode>*)NULL) {

	  if ( (level_num_a + 1) == (getNumLevels() - 1) ){
	    instance_valid_hyps_d.insertLast(curr_instance);

	    // go for next instance
	    //
	    continue;
	  }
	}

      
	// if we propagate new instances, but not sure they are pruned or not
	//
	if ( descendInstance(level_num_a, curr_instance) ) {
	  
	  // if all new instances are pruned
	  // bump the current instance off of the instance list
	  //
	  if (curr_instance->getRefCount() < 1) {
	    
	    if (search_mode_d == DECODE) {
	      Instance::deleteInstance(curr_instance, true);
	    }
	  }
	  else {

	    // now, there is new instance survived
	    //
	    status = true;
	  }
	  instance_lists_d(level_num_a).gotoFirst();
	}
      
	// else if no new instances can be generated.
	//
	else {

	  float curr_score = curr_instance->getScore();

	  // update the maximal instance score for the beam pruning
	  //
	  if(getSearchLevel(level_num_a).useBeam()
	     && (curr_score > max_instance_scores_d(level_num_a)) ) {
	    max_instance_scores_d(level_num_a) = curr_score;
	  }

	  // keep the instance on this level
	  //
	  instance_lists_d(level_num_a).insertLast(curr_instance);
	  instance_lists_d(level_num_a).gotoFirst();
	}
      }
    }
  }
  
  // exit gracefully
  //
  return status;
}

// method: networkDecoder
//
// arguments:
//  Vector<VectorFloat>& fe: (input) vector of inputs
//  long num_frames: (input) the number of steps to move forward
//
// return: logical error status
//
// decode part (or all) of the search space
//
boolean HierarchicalSearch::networkDecoder(Vector<VectorFloat>& fe_a,
				   long num_frames_a) {

  // if we haven't started the search then we need to initialize the
  // search
  //
  if ((long)current_frame_d == DEF_START_FRAME) {
    initializeNetworkDecoder();
  }

  // determine the number of frames to loop through. If the user specifies
  // to loop over all frames then the frame_num is set to a default value
  // so that the decoder loops indefinitely
  //
  long frame_num = 0;
  if (num_frames_a == DEF_NUM_FRAMES) {
    frame_num = num_frames_a - 1;
  }
  
  // loop over the specified number of frames
  //
  long size = fe_a.length();
  
  while (current_frame_d < size &&
	 features_d.assign(fe_a(current_frame_d)) &&
	 frame_num < num_frames_a) {

    // decode one frame of data
    //
    if (!traverseLexInstanceLevels()) {
      break;
    }
    
    // increment the frame_num if necessary - otherwise we loop indefinitely
    //
    if (num_frames_a != DEF_NUM_FRAMES) {
      frame_num++;
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: networkDecoder
//
// arguments:
//  Vector<FrontEnd>& vector_fe: (input) front-ends for extracting features
//  long num_frames_a: (input) the number of steps to move forward
//
// return: logical error status
//
// decode part (or all) of the search space
//
boolean HierarchicalSearch::networkDecoder(Vector<FrontEnd>& vector_fe_a,
					   long num_frames_a) {
  
  // if we haven't started the search then we need to initialize the
  // search
  //
  if ((long)current_frame_d == DEF_START_FRAME) {
    initializeNetworkDecoder();
  }

  // determine the number of frames to loop through. If the user specifies
  // to loop over all frames then the frame_num is set to a default value
  // so that the decoder loops indefinitely
  //
  long frame_num = 0;
  if (num_frames_a == DEF_NUM_FRAMES) {
    frame_num = num_frames_a - 1;
  }
  
  // loop over all the front ends except the last one that generates
  // the feature data
  //
  for (long i = 0; i < vector_fe_a.length() - 1; i++) {    
    vector_fe_a(i).getVector(features_d, 0, current_frame_d);
  }
  
  // loop over the specified number of frames
  // ISIP_BUG: (hamaker) the channel is set to zero here which won't be
  //           right for multi-channel data
  //
  while (vector_fe_a(vector_fe_a.length() - 1).getVector(features_d, 0,
							 current_frame_d) &&
	 frame_num < num_frames_a) {
    
    // ISIP_BUG: check the feature length here
    // decode one frame of data
    //
    if (!traverseLexInstanceLevels()) {
      break;
    }

    // increment the frame_num if necessary - otherwise we loop indefinitely
    //
    if (num_frames_a != DEF_NUM_FRAMES) {
      frame_num++;
    }
  }

  // exit gracefully
  //
  return true;
}

// method: initializeNetworkDecoder
//
// arguments: none
//
// return: logical error status
//
// initialize the lists, etc. needed to begin searching
//
boolean HierarchicalSearch::initializeNetworkDecoder() {

  // loccal variable
  //
  long num_levels = getNumLevels();

  // make sure that we have at least one level defined
  //
  if ( num_levels < 1) {
    return Error::handle(name(), L"initializeLexNetworkDecoder",
			 HierarchicalSearch::ERR_LEVEL, __FILE__, __LINE__);
  }

  // set the current frame
  //
  current_frame_d = 0; 

  // clear the valid_hyps_d list
  //
  clearInstanceStorage();
  
  // clear the instance lists of all the search nodes
  //
  clearSearchNodesInstanceLists();

  // clear the trellis before starting
  //
  if (search_mode_d == TRAIN) {
    return Error::handle(name(),
		 L"initializeLexNetworkDecoder: can't used in training mode",
		 HierarchicalSearch::ERR_LEVEL, __FILE__, __LINE__);
  }

  // initialize lexical trees
  //
  for (long i = 0; i < num_levels ; i++){
    if ( getSearchLevel(i).useLexicalTree()){
      expandLexicalTree(i);
    }
  }
  
  // generate initial instances for the search engine
  //
  networkLexStart();
  
  // exit gracefully
  //
  return true;
}

// method: networkLexStart
//
// arguments:
//
// return: logical error status
//
// start search engin with a network structure
//
boolean HierarchicalSearch::networkLexStart(){
  
  // seed the top instance list with the start node of the search graph
  //
  Instance* tmp_instance = new Instance();
  
  if (search_mode_d == TRAIN) {
    tmp_instance ->setInstanceMode(Instance::TRAIN);
  }
  tmp_instance ->setFrame((ulong)current_frame_d);

  // has the history been previously generated? if so reuse the history,
  // if not generate a new history and add it to the history pool
  //
  tmp_instance->setHistory(history_pool_d.initAndAllocate());
  tmp_instance->setSymbolStack(history_pool_d.initAndAllocate());
    
  // set the start vertex
  //
  GraphVertex<SearchNode>* tmp_vert
    = (*h_digraph_d)((long)initial_level_d).getSubGraph(0).getStart();

  // descend this original instance to level 0, that why we set
  // the level number to -1: (-1 + 1 = 0)
  //
  changeHistory(tmp_instance, false, -1,
		tmp_vert);

  // add the instance to the instance list
  //
  Instance* start_instance = (Instance*)NULL;

  // initialize the structure that holds the n-symbols
  //
  initializeNsymbolInstance(tmp_instance);

  // add to the instance list
  //
  addInstance(0, start_instance, tmp_instance, false);

  // exit gracefully
  //
  return true;
}

// method: traverseLexInstanceLevels
//
// arguments: none
//
// return: logical error status
//
// do a single-step traversal of the hypotheses at each level
//
boolean HierarchicalSearch::traverseLexInstanceLevels() {
  
  // define local variables
  //
  boolean status = false;
  
  // make sure we have some paths left to traverse
  //
  if (!pathsRemainInstance()) {
    return false;
  }

  // propagate instances forward until they are all ready to be evaluated
  //
  status = (propagateLexInstances() || status);

  // now we evaluate all instances that are in the lowest level and move 
  // them forward one arc. viterbi pruning takes place at this level 
  // as does beam pruning.
  //
  // lexical tree can't be at this level
  //
  status = (evaluateLexInstanceModels() || status);

  // exit gracefully
  //
  return status;
}

// method: propagateLexInstances
//
// arguments: none
//
// return: logical error status
//
// move instances through the hierarchy until all active instances are ready to be
// evaluated
//
boolean HierarchicalSearch::propagateLexInstances() {
  
  // define local variables
  //
  long level_num = 0;
  boolean status = true;
  long num_levels = getNumLevels();
  String out;
  
  // clear the hypothesis list since we are generating new hypotheses for this
  // frame
  //
  clearValidHypsLexInstance();

  // move all instances forward in the search space. we do this by
  // pushing instances up and down in the hierarchy until they all rest
  // in a state that is ready to be evaluated. if there is an infinite
  // "skip" loop in the search graph then this process will go into an
  // infinite loop. an infinite skip loop is of the form:
  // A -> B && B -> A.
  // this infinite loop structure may be masked by the fact that it 
  // extends over multiple search levels.
  // 
  // when we exit the while loop below, all instances should be in the lowest
  // level and should be ready for evaluation.
  //
  while ((getActiveInstances() != getActiveInstances(num_levels - 1)) ||
	 status) {
    
    // status indicates whether any movement in the instances happened
    //
    status = false;

    for (level_num = 0;  level_num < num_levels; level_num++) {
      if (getSearchLevel(level_num).useBeam()) {
	max_instance_scores_d(level_num) = Instance::INACTIVE_SCORE;
      }
    }
    
    // work from the bottom up until we reach a point where all instances
    // are ready to descend again. this segment takes care of
    // posterior scores such as nsymbol probabilities and applies
    // pruning (beam and instance) at each level.
    //
    for (level_num = num_levels - 1; level_num >= 0; level_num--) {
      
      if (debug_level_d >= Integral::DETAILED) {      
	String output;        
	output.assign(L"propagating instances up from level: ");
	output.concat(level_num);
	output.concat(L" in frame: ");    
	output.concat(current_frame_d);
	Console::put(output);
      }
      
      // propagate up
      //
      status = (propagateLexInstancesUp(level_num) || status);
    }
    
    // propagate instances down the hierarchy, carrying out viterbi pruning
    // as we go.
    //
    for (level_num = (long)initial_level_d;  level_num < num_levels - 1; level_num++) {
      
      // beam pruning
      //
      if (getSearchLevel(level_num).useBeam()) {

	if (debug_level_d >= Integral::DETAILED) {
	  Console::put(L"\nbeam pruning after propagation instances up");
	}
	
	beamPruneLexInstance(level_num);
      }

      // instance pruning
      //
      if (getSearchLevel(level_num).useInstance()) {

	if (debug_level_d >= Integral::DETAILED) {
	  Console::put(L"\ninstance pruning after propagation instances up");
	}
	
	instancePruneLexInstance(level_num);
      }      

      // print debug infomation
      //
      if (debug_level_d >= Integral::DETAILED) {
	String output;        
	output.assign(L"propagating instances down from level: ");
	output.concat(level_num);
	output.concat(L" in frame: ");    
	output.concat(current_frame_d);
	Console::put(output);
      }
      
      // propagate down
      //
      status = (propagateLexInstancesDown(level_num) || status);
    }
  }
  
  // print debugging information
  //
  if (debug_level_d >= Integral::ALL) {
    
    String val;
    val.assign(L"Frame:");
    val.concat(current_frame_d);
    val.concat(L" Instances left:");
    val.concat(getActiveInstances(num_levels - 1));
    Console::put(val);
  }
  
  // exit gracefully
  //
  return true;
}

// method: expandLexicalTree
//
// arguments:
//
// return: logical error status
//
// expand graphs into lexical trees
//
boolean HierarchicalSearch::expandLexicalTree(long level_num_a) {

  // check with the lexical tree level
  //
  if ( level_num_a > ( getNumLevels() - 1)
       || level_num_a < 0 ) {
    return Error::handle(name(),
			 L"expandLexicalTree: wrong lexical tree level",
			 HierarchicalSearch::ERR_LEVEL, __FILE__, __LINE__);
  }

  // build the lexical tree from top to down
  //
  for (long i  = 0; i < (*h_digraph_d)(level_num_a).getSubGraphs().length();
       i++ ){
    DiGraph<SearchNode>& word_graph =
      (*h_digraph_d)(level_num_a).getSubGraph((long)i);

    //h_digraph_d(level_num_a+1).debug(L"h_digraph_d");
    //h_digraph_d(level_num_a+1).getSubGraphs().debug(L"sub_graphs_d");
    
    // expand the top level graph to a lexical tree
    //
    LexicalTree::expandLexicalTree(word_graph,
				 (*h_digraph_d)(level_num_a+1).getSubGraphs(),
				 level_num_a);
    
  }
  
  // exit gracefully
  //
  return true;
}

// method: getLexHypotheses
//
// arguments:
//  String& output_hyp: (output) the current search hypotheses
//  long level: (input) the level to print hypotheses from
//  float& total_score: (output) the hypothesis total score
//  long& num_frames: (output) frame index of the last instance
//  DoubleLinkedList<Instance>& instance_path: (output) best hypothesis instance path
//
// return: logical error status
//
// build a graph representing the hypotheses and return it
//
boolean HierarchicalSearch::getLexHypotheses(String& output_hyp_a,
					  long level_a,
					  float& total_score_a,
					  long& num_frames_a,
					  DoubleLinkedList<Instance>& instance_path_a) {

  // declare local variables
  //
  Instance* tmp_instance = (Instance*)NULL;
  SearchNode*  prev_node = (SearchNode*)NULL;
  SearchNode*  tmp_node = (SearchNode*)NULL;
  
  long counter = 0;
  long curr_level = 0;
  long frame_ind = 0;
  long prev_frame_ind = -1;
  long back_count = 0;
  float score = 0.0;
  float prev_score = 0.0;
  long symbol_id = 0;
  
  String out_str;
  SearchSymbol sym;  
   
  // clear the output and set the allocation mode for the instance path
  //
  output_hyp_a.clear();

  instance_path_a.clear();
  instance_path_a.setAllocationMode(DstrBase::USER);
  
  // move all instances forward in the search space
  //
  if (!propagateLexInstances()) {
    return false;
  }

  // make sure we have at least one valid hypothesis
  //  
  if (instance_valid_hyps_d.length() < 1) {
    return false;
  }
  
  // loop over all valid instances in the hypothesis list
  //
  instance_valid_hyps_d.gotoFirst();
  Instance* best_end_hyp = instance_valid_hyps_d.getCurr();
  
  // find the instance with the best score, i.e., best hypothesis instance
  //
  while (true) {
  
    if (!instance_valid_hyps_d.gotoNext()) {
      break;
    }

    tmp_instance = instance_valid_hyps_d.getCurr();
    if (tmp_instance->getScore() > best_end_hyp->getScore()) {
      best_end_hyp = tmp_instance;
    }
  }
  
  // print debugging information
  //
  if (debug_level_d >= Integral::DETAILED) {
    
    // print the best instance
    //
    printInstance(best_end_hyp, -1, true);
  }
  
  // backtrack from the best hypothesis instance and generate the instance path
  //
  for (tmp_instance = best_end_hyp; tmp_instance != (Instance*)NULL;
       tmp_instance = tmp_instance->getBackPointer()) {
    instance_path_a.insertFirst(tmp_instance);
    back_count++;
  }

  // use the best hypothesis instance path to generate the hypothesis string
  //
  for (boolean more_instances = instance_path_a.gotoFirst(); more_instances;
       more_instances = instance_path_a.gotoNext()) {

    counter++;
    tmp_instance = instance_path_a.getCurr();
    frame_ind = tmp_instance->getFrame();

    // get the central vertex from the top of the history stack
    //
    GraphVertex<SearchNode>* tmp_vertex = (GraphVertex<SearchNode>*)NULL;
    tmp_vertex = tmp_instance->getSymbol()->getCentralVertex();

    // check for a NULL vertex
    //
    if (tmp_vertex == (GraphVertex<SearchNode>*)NULL) {
      	prev_score = tmp_instance->getScore();
	score = prev_score;
    }
    
    // make sure the vertex is neither the dummy
    // start nor terminating node
    //
    else if ((!tmp_vertex->isStart()) &&
	(!tmp_vertex->isTerm())) {

      tmp_node = tmp_vertex->getItem();
      curr_level = tmp_node->getSearchLevel()->getLevelIndex();

      if ((level_a < 0) && (curr_level == 0)) {

	tmp_node->getSymbol(sym);
	score = tmp_instance->getScore();
	symbol_id = tmp_node->getSymbolId();
	
	// generate the output hypothesis containing only search symbols
	//
	if (!getSearchLevel(curr_level).isExcludeSymbol(symbol_id)) {
	  out_str.assign(sym);
	  out_str.concat(L" ");
	}
	else {
	  out_str.assign(L"");
	}
	
	// append partial string to the complete hypothesis report
	//
	if ( frame_ind != prev_frame_ind ) {
	  output_hyp_a.concat(out_str);
	}
	
	prev_node = tmp_node;
	prev_score = score;
	prev_frame_ind = frame_ind;
      }
      
      if ((level_a >= 0) && (curr_level == level_a)) {

	String context;
	tmp_instance->getSymbol()->print(context);
	score = tmp_instance->getScore();
	sym.assign(context);
	
	// generate the output hypothesis
	//
	if (counter > 1) {
	  out_str.assign(L"\n");
	} else {
	  out_str.assign(L"");
	}

	// append previous frame index
	//
	if (level_a != (getNumLevels() - 1)) {
	  out_str.concat(prev_frame_ind);
	}
	else {
	  out_str.concat(prev_frame_ind + 1);
	}
	out_str.concat(L"\t");

	// append current frame index
	//
	if (level_a != (getNumLevels() - 1)) {
	  out_str.concat(frame_ind);
	}
	else {
	  out_str.concat(frame_ind + 1);
	}
	out_str.concat(L"\t");
	
	// append search symbol
	//
	out_str.concat(sym);
	out_str.concat(L"\t\t");
	
	// append score
	//
	out_str.concat(score - prev_score);
	
	// append partial string to the complete hypothesis report
	//
	if (level_a == (getNumLevels() - 1)) {
	  output_hyp_a.concat(out_str);
	}
	else {
	  if ( frame_ind != prev_frame_ind ) {
	    output_hyp_a.concat(out_str);
	  }
	}	
	
	prev_node = tmp_node;
	prev_score = score;
	prev_frame_ind = frame_ind;
      }      
    }    
  }

  total_score_a = score;
  num_frames_a = frame_ind;
  
  // exit gracefully
  //
  return true;
}

// method: evaluateLexInstanceModels
//
// arguments: (none)
//
// return: logical error status.
//
// evaluate all statistical models
//
boolean HierarchicalSearch::evaluateLexInstanceModels() {

  // define local variables
  //
  String out;
  String val;
  long level_num = getNumLevels() - 1;
  boolean end_loop = false;
  Instance* curr_instance = (Instance*)NULL;
  Instance* next_instance = (Instance*)NULL;  
  GraphVertex<SearchNode>* curr_vert = (GraphVertex<SearchNode>*)NULL;
  SearchSymbol item;
 
  // output the frame information
  //
  if (debug_level_d >= Integral::DETAILED) {
    out.assign(L"\n-> evaluating data in frame: ");
    val.assign((ullong)current_frame_d);
    out.concat(val);
    Console::put(out);    
  }

  // if the beam pruning is required at this level
  //    initialize the maximal instance score
  //
  if(getSearchLevel(level_num).useBeam()) {
    max_instance_scores_d(level_num) = Instance::INACTIVE_SCORE;
  }

  // loop over all active instances and score them
  //
  for (boolean more_instances = instance_lists_d(level_num).gotoFirst();
       more_instances;
       more_instances = instance_lists_d(level_num).gotoNext()) {
    
    // get the current vertex in the search graph
    //
    curr_instance = instance_lists_d(level_num).getCurr();
    curr_vert = curr_instance->getSymbol()->getCentralVertex();

    // evaluate the statistical model
    //    
    float new_score = curr_instance->getScore() +
      curr_vert->getItem()->evaluateData(features_d, (long)current_frame_d);
    curr_instance->setScore(new_score);
    
    // update the maximal instance score for the beam pruning
    //
    if(getSearchLevel(level_num).useBeam()
       && (new_score > max_instance_scores_d(level_num))) {
      max_instance_scores_d(level_num) = new_score;
    }
  }

  // beam pruning at this level
  //
  if (getSearchLevel(getNumLevels() - 1).useBeam()) {

    if (debug_level_d >= Integral::DETAILED) {
      Console::put(L"\nbeam pruning after propagation instances up");
    }
    
    beamPruneLexInstance(getNumLevels() - 1);
  }

  // instance pruning at this level
  //
  if (getSearchLevel(getNumLevels() - 1).useInstance()) {

    if (debug_level_d >= Integral::ALL) {
      Console::put(L"\ninstance npruning after propagation instances up");
    }
    
    instancePruneLexInstance(getNumLevels() - 1);
  }  
  
  // update current frame
  //        - instances generated later will correspond to the next frame
  //
  current_frame_d += 1 ;

  // print debugging information
  //
  if (debug_level_d >= Integral::DETAILED) {
    out.assign(L"\n-> time changed to: ");
    out.concat(current_frame_d);
    Console::put(out);
  }
  
  // we mark the current end of the list so we only propagate those instances
  // forward that were just evaluated
  //
  instance_lists_d(level_num).gotoLast();
  instance_lists_d(level_num).setMark();
  instance_lists_d(level_num).gotoFirst();

  // print debugging information
  //
  if (debug_level_d >= Integral::DETAILED) {
    Console::put(L"\npropagating instances forward after the model evaluation");
  }
  
  // loop until we have propagated all evaluated instances forward
  //
  end_loop = instance_lists_d(level_num).isEmpty();
  while (!end_loop) {
    
    end_loop = instance_lists_d(level_num).isMarkedElement();
    
    // get the current vertex in the search graph
    //
    curr_instance = instance_lists_d(level_num).getCurr();
    curr_vert = curr_instance->getSymbol()->getLastVertex();
    
    // if this instance is inactive, then delete it
    //
    if (!curr_instance->isActive()) {
      
      // bump the current instance off of the instance list
      //
      if (debug_level_d >= Integral::ALL) {
	Console::put(L" not active");
      }      
      instance_lists_d(level_num).removeFirst(curr_instance);
      Instance::deleteInstance(curr_instance, true);
    }
    
    // else, extend paths and apply viterbi pruning
    //
    else {
      
      // extend all paths from that vertex and apply viterbi pruning
      //
      for (boolean more_paths = curr_vert->gotoFirst(); more_paths;
	   more_paths = curr_vert->gotoNext()) {
	
	// create a new instance
	//
	next_instance = new Instance(*curr_instance);
	next_instance->setFrame((ulong)current_frame_d);

	// determine the symbol insertion penalty and transition scale factor
	//
	float tr_scale = getSearchLevel(level_num).getTrScale();
	
	// update the score with the arc score
	//
	GraphArc<SearchNode>* tmp_arc = curr_vert->getCurr();
	float transition_score = tr_scale * tmp_arc->getWeight();
	next_instance->setScore(next_instance->getScore() + transition_score);
	
	// update the history with the next node at this level
	//
	next_instance->setSymbol(context_pool_d.shiftAndAllocate(next_instance->getSymbol(), tmp_arc->getVertex()));

	// set the backpointer
	//
	next_instance->setBackPointer(curr_instance);
	  
	// apply n-symbol score
	//
	applyNSymbolScore(next_instance, level_num);

	// set the language model probability
	//
	if (getSearchLevel(level_num).useRescore()) {
	  next_instance->setLMScore(tmp_arc->getLanguageWeight());
	}
	
	// add the instance to the search node's instance list
	//
	Instance* existing_instance = (Instance*)NULL;
	SearchNode* next_node = next_instance->getSymbol()->
	getCentralVertex()->getItem();
 
	if (next_node->addInstance(next_instance, current_frame_d, existing_instance)) {
	  	
	  // add the instance to this level's instance list
	  //
	  instance_lists_d(level_num).insertLast(next_instance);

	  // print debugging information
	  //
	  if (debug_level_d >= Integral::DETAILED) {
	    
	    // print the best instance
	    //
	    printInstance(next_instance, level_num);
	  }
    
	  // insert the trace in the trellis
	  //
	  if (search_mode_d == TRAIN) {
	    if (!insertNewPath(curr_instance, next_instance, tmp_arc->getWeight())) {
	      return Error::handle(name(), L"evaluateModels", Error::ARG, __FILE__, __LINE__);
	    }
	  }	  
	}
	else {

	  // delete the instance since there is an existing instance
	  // - same history
	  //
	  // print debugging information
	  //
	  if (debug_level_d >= Integral::DETAILED) {
	    
	    // print the best instance
	    //
	    printInstance(next_instance, level_num);
	  }
    
	  delete next_instance;	  

	  if (search_mode_d == TRAIN) {
	    if (existing_instance != (Instance*)NULL) {
	      if (!insertOldPath(curr_instance, existing_instance, tmp_arc->getWeight())) {
		return Error::handle(name(), L"evaluateModels", Error::ARG, __FILE__, __LINE__);
	      }
	    }
	    else {
	      return Error::handle(name(), L"existing instance", Error::ARG, __FILE__, __LINE__);
	    }
	  }
	}
      }
      
      // bump the current instance off of the instance list
      //
      instance_lists_d(level_num).removeFirst(curr_instance);
      if (curr_instance->getRefCount() < 1) {
	Instance::deleteInstance(curr_instance, true);
      }
      instance_lists_d(level_num).gotoFirst();
      
    } // end if the instance is active
  }   // end while (!end_loop)
  
  // exit gracefully
  //
  return true;  
}

// method: instancePruneLexInstance
//
// arguments:
//  long level_num: (input) the level for which we will propagate traces
//
// return: logical error status.
//
boolean HierarchicalSearch::instancePruneLexInstance(long level_num_a) {

  // define local variables
  //
  Instance* curr_instance = (Instance*)NULL;
  long threshold = (*h_digraph_d)(level_num_a).getInstanceThreshold();

  long pruned_instances = 0;
  long orig_length = instance_lists_d(level_num_a).length();

  // check if we need to prune any instances at this level
  //
  if (orig_length > threshold) {
    long num_to_remove = orig_length - threshold;

    // sort the instances
    //
    instance_lists_d(level_num_a).sort(Integral::ASCENDING,
				       DstrBase::RAND_QUICK);
    
    // loop over all active instances at this level
    //
    for (long i=0; i < num_to_remove; i++) {

      instance_lists_d(level_num_a).remove(curr_instance);

      if (curr_instance->getRefCount() < 1) {
	Instance::deleteInstance(curr_instance, true);
      }
      
      pruned_instances++;
    }
  }

  if (debug_level_d >= Integral::DETAILED) {
    String output(L"frame: ");
    output.concat(current_frame_d);
    output.concat(L", level: ");
    output.concat(level_num_a);
    output.concat(L", original instances: ");
    output.concat(orig_length);
    output.concat(L", pruned instances: ");
    output.concat(pruned_instances);
    output.concat(L", current instances: ");
    output.concat(instance_lists_d(level_num_a).length());
    output.concat(L", threshold: ");
    output.concat(threshold);
    output.concat(L"\n");
    Console::put(output);
  }
  
  // exit gracefully
  //
  return true;    
}

// method: beamPruneLexInstance
//
// arguments:
//  long level_num: (input) the level for which we will propagate traces
//
// return: logical error status.
//
boolean HierarchicalSearch::beamPruneLexInstance(long level_num_a) {

  // define local variables
  //
  Instance* curr_instance = (Instance*)NULL;
  float threshold = max_instance_scores_d(level_num_a)
    - (*h_digraph_d)(level_num_a).getBeamThreshold();
  float curr_score;

  long pruned_instances = 0;
  long orig_length = instance_lists_d(level_num_a).length();
  
  // loop over all active instances at this level
  //
  boolean more_instances = instance_lists_d(level_num_a).gotoFirst();
  
  while (more_instances) {

    curr_instance = instance_lists_d(level_num_a).getCurr();
    curr_score = curr_instance->getScore();

    if (curr_score > max_instance_scores_d(level_num_a)) {
      return Error::handle(name(), L"beamPruneLexInstance - bad maximal score", Error::ARG, __FILE__, __LINE__);
    }
      
    // prune the instances with too low score
    //
    if (curr_score < threshold)  {

      if (debug_level_d >= Integral::ALL) {
	String output(L"score is smaller than threshold: ");
	output.concat(curr_score);
	output.concat(L", instance:" );
	output.concat(curr_instance);
	Console::put(output);
      }

      if (instance_lists_d(level_num_a).isLast()) {
	more_instances = false;
      }
	  
      // bump the current instance off of the instance list
      //
      instance_lists_d(level_num_a).remove();
      
      if (curr_instance->getRefCount() < 1) {
	Instance::deleteInstance(curr_instance, true);
      }
      pruned_instances++;
    }
    
    else {

      if (debug_level_d >= Integral::ALL) {
	String output(L"score is not smaller than threshold: ");
	output.concat(curr_score);
	output.concat(L", instance:" );
	output.concat(curr_instance);
	Console::put(output);
      }
      
      more_instances = instance_lists_d(level_num_a).gotoNext();
    }    
  }

  if (debug_level_d >= Integral::DETAILED) {
    String output(L"frame: ");
    output.concat(current_frame_d);
    output.concat(L", level: ");
    output.concat(level_num_a);
    output.concat(L", original instances: ");
    output.concat(orig_length);
    output.concat(L", pruned instances: ");
    output.concat(pruned_instances);
    output.concat(L", current instances: ");
    output.concat(instance_lists_d(level_num_a).length());
    output.concat(L", threshold: ");
    output.concat(threshold);
    output.concat(L", max instance score: ");
    output.concat(max_instance_scores_d(level_num_a));
    output.concat(L"\n");
    Console::put(output);
  }
  
  // exit gracefully
  //
  return true;
}

// method: clearValidHypsLexInstance
//
// arguments: none
//
// return: logical error status
//
// clear the instances stored in the valid hypothesis list
//
boolean HierarchicalSearch::clearValidHypsLexInstance() {

   // define local variables
  //
  Instance* tmp_instance;
  
  // use the trace_instance_valid_hyps_d list as temporary storage
  //
  while (instance_valid_hyps_d.removeFirst(tmp_instance)) {
    if (tmp_instance != (Instance*)NULL) {
      Instance::deleteInstance(tmp_instance, true);
    }
  }
 
  // exit gracefully
  //
  return true;
}

// method: convertLexInstances
//
// arguments:
//  DoubleLinkedList<Instance>& instance_path: (output) best hypothesis instance path
//
// return: logical error status
//
// build a graph representing the hypotheses and return it
//
boolean HierarchicalSearch::convertLexInstances( DoubleLinkedList<Instance>&
						 instance_path_a) {

  // declare local variables
  //
  Instance* tmp_instance = (Instance*)NULL;
  Instance* prev_instance = (Instance*)NULL;
  Instance* term_instance = (Instance*)NULL;
  Instance* start_instance = (Instance*)NULL;
  Instance* end_instance = (Instance*)NULL;
  
  GraphVertex<SearchNode>* tmp_vertex = (GraphVertex<SearchNode>*)NULL;
  GraphVertex<SearchNode>* start_vertex = (GraphVertex<SearchNode>*)NULL;
  GraphVertex<SearchNode>* end_vertex = (GraphVertex<SearchNode>*)NULL;

  SearchNode*  tmp_node = (SearchNode*)NULL;

  if (debug_level_d >= Integral::DETAILED) {

    // debug message
    //
    Console::put(L"\nBefore conversion:  ---------------------------");
    printInstances(instance_path_a);
  }
  // store the instances at different levels
  //
  long num_levels = getNumLevels();
  Vector< DoubleLinkedList<Instance> > inst_levels;
  Vector< DoubleLinkedList<Instance> > prev_levels;
  inst_levels.setLength( num_levels );
  prev_levels.setLength( num_levels );

  long curr_level = 0;
  long prev_level = 0;
  long frame_ind = 0;
  
  String out_str;
  SearchSymbol sym;  

  // find the start and term instances and also set start and end
  // vertex so that they can be used as marks later.
  //
  for (boolean more_instances = instance_path_a.gotoFirst(); more_instances;
       more_instances = instance_path_a.gotoNext()) {

    // if we found the start and end instance, we will use it as mark later
    // 
    if ( start_instance != NULL && end_instance != NULL ){
      break;
    }

    tmp_instance = instance_path_a.getCurr();

    // get the central vertex from the top of the history stack
    //
    GraphVertex<SearchNode>* tmp_vertex = (GraphVertex<SearchNode>*)NULL;
    tmp_vertex = tmp_instance->getSymbol()->getCentralVertex();

    // check for a NULL vertex
    //
    if (tmp_vertex == (GraphVertex<SearchNode>*)NULL) {
      continue;
    }
  
    // set the start instance
    //
    if ( tmp_vertex->isStart() ){
      start_instance = tmp_instance;
      start_vertex = tmp_vertex;
    }

    // set the end instance
    //
    if ( tmp_vertex->isTerm() ){
      end_instance = tmp_instance;
      end_vertex = tmp_vertex;
    }
    
  }

  // initialize the instance levels and vertex levels
  //
  for (long i = 0; i < num_levels; i++){

    // init the instance levels
    //
    DoubleLinkedList<Instance>* instance_path
      = new DoubleLinkedList<Instance>;
    instance_path->setAllocationMode(DstrBase::USER);
    instance_path->insertFirst(start_instance);
    inst_levels.concat(*instance_path);

    // free allocated memory
    //
    delete instance_path;
    instance_path = (DoubleLinkedList<Instance>*)NULL;
    
    // init the vertex levels
    //
    DoubleLinkedList<Instance>* prev_path
      = new DoubleLinkedList< Instance >;
    prev_path->setAllocationMode(DstrBase::USER);
    prev_levels.concat(*prev_path);

    // free allocated memory
    //
    delete prev_path;
    prev_path = (DoubleLinkedList<Instance>*)NULL;
  }
  
  // use the best hypothesis instance path to generate the hypothesis string
  //
  for (boolean more_instances = instance_path_a.gotoFirst(); more_instances;
       more_instances = instance_path_a.removeFirst(tmp_instance)) {

    frame_ind = tmp_instance->getFrame();
    
    // get the central vertex from the top of the history stack
    //
    tmp_vertex = (GraphVertex<SearchNode>*)NULL;
    tmp_vertex = tmp_instance->getSymbol()->getCentralVertex();

    // check for a NULL vertex
    //
    if (tmp_vertex == (GraphVertex<SearchNode>*)NULL) {
      continue;
    }

    if (tmp_vertex->isTerm()){
      term_instance = tmp_instance;
      continue;
    }
    
    // make sure the vertex is neither the dummy
    // start nor terminating node
    //
    else if ((!tmp_vertex->isStart()) &&
	(!tmp_vertex->isTerm())) {
      
      tmp_node = tmp_vertex->getItem();
      curr_level = tmp_node->getSearchLevel()->getLevelIndex();

      // skip the dummy node
      //
      if ( tmp_node->isDummyNode() ){
	continue;
      }
      
      // add to the instance list
      //
      if ( curr_level == num_levels - 1 ){

	inst_levels(curr_level).insertLast(tmp_instance);
	//prev_levels(curr_level).insertLast(NULL);
      }

      // prev instance is lower than current one
      //
      else if ( prev_level == curr_level + 1 ){
	inst_levels(curr_level).insertLast(tmp_instance);
	prev_levels(curr_level).insertLast(prev_instance);
      }

      // prev instance is term
      //
      else if ( term_instance != NULL && curr_level == prev_level ){
	inst_levels(curr_level).insertLast(tmp_instance);
	prev_levels(curr_level).insertLast(term_instance);	
      }
    }

    prev_level = curr_level;
    prev_instance = tmp_instance;
    term_instance = NULL;
  }

  instance_path_a.clear();
  instance_path_a.setAllocationMode(DstrBase::SYSTEM);

  // re-order the instance list
  //
  prev_levels(0).gotoFirst();
  tmp_instance = new Instance(*start_instance);
  instance_path_a.insertLast(tmp_instance);
  frame_ind = 0;

  // free allocated memory
  //
  delete tmp_instance;
  tmp_instance = (Instance*)NULL;
  
  // init the inst and prev levels
  //
  for ( int i = 0 ; i < num_levels; i++ ){

    inst_levels(i).gotoFirst();
    prev_levels(i).gotoFirst();

    if (debug_level_d >= Integral::DETAILED) {
      // debug message
      //
      String tmp_string;
      Long l = i;
      tmp_string.assign(L"inst_levels:  --------------------------- ");
      tmp_string.concat(l);
      Console::put(tmp_string);
      printInstances(inst_levels(i));
    
      tmp_string.assign(L"prev_levels:  --------------------------- ");
      tmp_string.concat(l);
      Console::put(tmp_string);
      printInstances(prev_levels(i));

      inst_levels(i).gotoFirst();
      prev_levels(i).gotoFirst();
    }
  }
  
  for (boolean more_instances = inst_levels(0).gotoFirst(); more_instances;
       more_instances = inst_levels(0).gotoNext()) {

    tmp_instance = inst_levels(0).getCurr();
    if ( prev_levels(0).length() == 0 ){
      prev_instance = (Instance*)NULL;
    }
    else{
      prev_instance = prev_levels(0).getCurr();
    }
    addToPath(tmp_instance, prev_instance, inst_levels, prev_levels, 0, frame_ind, instance_path_a);
    prev_levels(0).gotoNext();
    
  }
  
  tmp_instance = new Instance(*end_instance);
  instance_path_a.insertLast(tmp_instance);  

  // free allocated memory
  //
  delete tmp_instance;
  tmp_instance = (Instance*)NULL;
  
  if (debug_level_d >= Integral::DETAILED) {

    // debug message
    //
    Console::put(L"\nAfter conversion:  ---------------------------");
    printInstances(instance_path_a);
  }
  // exit gracefully
  //
  return true;
}

// method: addToPath
//
// arguments:
//  DoubleLinkedList<Instance>& instance_path: (output) best hypothesis instance path
//
// return: logical error status
//
// build a graph representing the hypotheses and return it
//
boolean HierarchicalSearch::addToPath( Instance*& curr_inst_a,
				       Instance*& prev_inst_a,
				       Vector< DoubleLinkedList<Instance> >& inst_levels,
				       Vector< DoubleLinkedList<Instance> >& prev_levels,
				       long level_num_a,
				       long& frame_ind_a,
				       DoubleLinkedList<Instance>& instance_path_a) {

  // local variable
  //
  Instance* tmp_instance = (Instance*)NULL;
  Instance* prev_instance = (Instance*)NULL;
  ulong prev_frame_id = 0;

  GraphVertex<SearchNode>* tmp_vertex = (GraphVertex<SearchNode>*)NULL;
  GraphVertex<SearchNode>* prev_vertex = (GraphVertex<SearchNode>*)NULL;

  tmp_instance = new Instance(*curr_inst_a);

  if ( prev_inst_a == (Instance*)NULL ){

    frame_ind_a = tmp_instance->getFrame();
    instance_path_a.insertLast(tmp_instance);  

    // free allocated memory
    //
    delete tmp_instance;
    tmp_instance = (Instance*)NULL;
    
    // exit gracefully
    //
    return true;
  }
  
  tmp_instance->setFrame(frame_ind_a);
  instance_path_a.insertLast(tmp_instance);  

  // free allocated memory
  //
  delete tmp_instance;
  tmp_instance = (Instance*)NULL;
  
  // make sure there is a valid history
  //
  prev_frame_id = prev_inst_a->getFrame();
  
  // set the correct vertex
  //
  if ( prev_inst_a->getInstanceState() == Instance::PSEUDO_ACTIVE &&
       getSearchLevel(level_num_a + 1 ).getRightContext() > 0 ){
    prev_vertex = (GraphVertex<SearchNode>*)prev_inst_a->getSkipSymbol();
  }
  else {
    prev_vertex = prev_inst_a->getSymbol()->getCentralVertex();
  }

  //prev_vertex = tmp_hist->peek()->getCentralVertex();
  
  // for every instance up to the prev_instance
  //
  for (boolean more_instances = inst_levels(level_num_a+1).getCurr();
       more_instances && !prev_vertex->isTerm();
       more_instances = inst_levels(level_num_a+1).gotoNext()) {

    tmp_instance = inst_levels(level_num_a+1).getCurr();
    if ( prev_levels(level_num_a+1).length() == 0 ){
      prev_instance = NULL;
    }
    else{
      prev_instance = prev_levels(level_num_a+1).getCurr();
    }
    addToPath(tmp_instance, prev_instance,
	      inst_levels, prev_levels, level_num_a+1,
	      frame_ind_a, instance_path_a);
    
    // get the central vertex from the top of the history stack
    //
    tmp_vertex = (GraphVertex<SearchNode>*)NULL;
    tmp_vertex = tmp_instance->getSymbol()->getCentralVertex();

    // debug message
    //
    if (debug_level_d >= Integral::ALL) {
      if ( level_num_a == 0 ){
	Long curr_address((ulong)tmp_vertex);
	Long prev_address((ulong)prev_vertex);
	curr_address.debug(L"curr_address");
	prev_address.debug(L"prev_address");
	Long prev_frame(prev_frame_id);
	Long curr_frame(tmp_instance->getFrame());
	prev_frame.debug(L"prev_frame");
	curr_frame.debug(L"curr_frame");
      }
    }
    
    if ( tmp_vertex == prev_vertex &&
	 prev_frame_id <= tmp_instance->getFrame()){
      prev_levels(level_num_a+1).gotoNext();
      inst_levels(level_num_a+1).gotoNext();
      break;
    }

    // set to next one
    //
    prev_levels(level_num_a+1).gotoNext();
    
  }

  //inst_levels(level_num_a+1).gotoNext();
  
  // add the term instance
  //
  tmp_instance = new Instance(*curr_inst_a);
  tmp_instance->setFrame(frame_ind_a);
  instance_path_a.insertLast(tmp_instance);  

  // free allocated memory
  //
  delete tmp_instance;
  tmp_instance = (Instance*)NULL;
  
  // exit gracefully
  //
  return true;
  
}

// method: printInstances
//
// arguments:
//  DoubleLinkedList<Instance>& instance_path: (output) best hypothesis instance path
//
// return: logical error status
//
// build a graph representing the hypotheses and return it
//
boolean HierarchicalSearch::printInstances( DoubleLinkedList<Instance>&
						 instance_path_a) {

  // declare local variables
  //
  Instance* tmp_instance = (Instance*)NULL;
  GraphVertex<SearchNode>* tmp_vertex = (GraphVertex<SearchNode>*)NULL;
  SearchNode*  tmp_node = (SearchNode*)NULL;

  long curr_level = 0;
  long frame_ind = 0;
  
  String out_str;

  // find the start and term instances and also set start and end
  // vertex so that they can be used as marks later.
  //
  for (boolean more_instances = instance_path_a.gotoFirst(); more_instances;
       more_instances = instance_path_a.gotoNext()) {

    tmp_instance = instance_path_a.getCurr();

    // get the central vertex from the top of the history stack
    //
    tmp_vertex = (GraphVertex<SearchNode>*)NULL;
    tmp_vertex = tmp_instance->getSymbol()->getCentralVertex();

    // check for a NULL vertex
    //
    if (tmp_vertex == (GraphVertex<SearchNode>*)NULL) {
      continue;
    }

    // make sure the vertex is neither the dummy
    // start nor terminating node
    //
    else if ((!tmp_vertex->isStart()) &&
	(!tmp_vertex->isTerm())) {
      
      tmp_node = tmp_vertex->getItem();
      curr_level = tmp_node->getSearchLevel()->getLevelIndex();
      frame_ind = tmp_instance->getFrame();
      
      // print the context
      //
      String context;
      tmp_instance->getSymbol()->print(context);
      out_str.assign(context);

      out_str.concat(L" level:");
      out_str.concat(curr_level);

      out_str.concat(L" frame:");
      out_str.concat(frame_ind);
      Console::put(out_str);
    }
  }

  // exit gracefully
  //
  return true;
}
