// file: $isip/class/search/HierarchicalSearch/hsrch_11.cc
//
 
// isip include files
//
#include "HierarchicalSearch.h"

// method: addHypothesisPath
//
// arguments:
//   Trace*& new_trace: (input) new input trace
//   Trace*& old_trace: (input) old input trace
//   GraphArc<SearchNode>*& arc: (input) graph transition arc
//   long level_num: (input) current level number
//
// return: logical error status
//
// this method add an trace to the hypothesis path
//
boolean HierarchicalSearch::addHypothesisPath(Trace*& new_trace_a,
					      Trace*& old_trace_a,
					      GraphArc<SearchNode>*& arc_a,
					      long level_num_a) {
  
  // declare local variables
  //
  Context* symbol = (Context*)NULL;
  Trace* existing_trace = (Trace*)NULL;
  SearchNode* search_node = (SearchNode*)NULL;  
  GraphVertex<SearchNode>* v = (GraphVertex<SearchNode>*)NULL;

  // retrieve the transition scores for the corresponding transition
  //
  float weight = arc_a->getWeight();
  
  // retrieve the symbol penalty and transition scale for this level
  //
  float tr_scale = getSearchLevel(level_num_a).getTrScale();
  float symbol_penalty = getSearchLevel(level_num_a).getSymbolPenalty();

  // retrieve the symbol, search node and central vertex for this trace
  //
  if (((symbol = new_trace_a->getSymbol()) == (Context*)NULL) ||
      ((v = symbol->getCentralVertex()) == (GraphVertex<SearchNode>*)NULL) ||
      ((search_node = v->getItem()) == (SearchNode*)NULL)) {
    return false;
  }
  
  // add the transition score and symbol insertion penalty (when applicable)
  //
  long symbol_id = search_node->getSymbolId();
  
  if (!getSearchLevel(level_num_a).isDummySymbol(symbol_id) &&
      !getSearchLevel(level_num_a).isSPenaltyExcludeSymbol(symbol_id)) {
    new_trace_a->setScore(new_trace_a->getScore() +
			  (weight * tr_scale) + symbol_penalty);
  }
  else {
    new_trace_a->setScore(new_trace_a->getScore() + (weight * tr_scale));
  }
  
  // add any posterior score such as n-symbol probabilities
  //
  if (getSearchLevel(level_num_a).useNSymbol()) {
    
    if (!getSearchLevel(level_num_a).useRescore() &&
	!getSearchLevel(level_num_a).isDummySymbol(symbol_id) &&
	!getSearchLevel(level_num_a).isNSymbolExcludeSymbol(symbol_id) &&
	!v->isStart() && !v->isTerm()) {
      
      shiftNSymbol(new_trace_a, level_num_a, v);
      
      float posterior_score =
	getPosteriorScore(new_trace_a->getNSymbol(), level_num_a);
      
      new_trace_a->setScore(new_trace_a->getScore() + posterior_score);
      new_trace_a->setLMScore(posterior_score);
    }
  }

  // when we are in acoustic rescoring mode we need to read the language
  // model probabilities directly from the symbol graph
  //
  if (getSearchLevel(level_num_a).useRescore()) {

    // retrieve the lm score and the lm scale factor
    //
    float lm_score = arc_a->getLanguageWeight();
    float lm_scale = symbol_graph_d.getScale();

    // add the scaled lm score and symbol insertion penalty
    //
    new_trace_a->setScore(new_trace_a->getScore() + (lm_scale * lm_score));
    
    if (getSearchLevel(level_num_a).useAcousticScore()) {
      
      // retrieve the ac score and the ac scale factor
      //
      float ac_score = arc_a->getAcousticWeight();
      float ac_scale = getSearchLevel(level_num_a).getAcousticScale();

      new_trace_a->setScore(new_trace_a->getScore() + (ac_score / ac_scale));
    }    
  }
  
  // apply viterbi pruning at this level if we are not applying nsymbol
  // probabilities and we are not generating symbol graphs
  //
  if (!getSearchLevel(level_num_a).useSymbolGraph() &&
      !getSearchLevel(level_num_a).useNSymbol()) {
    
    // add the trace to the search node's trace list
    //
    if (search_node->addTrace(new_trace_a, current_frame_d,
			      existing_trace)) {
      
      // update the maximal trace score for the beam pruning
      //
      float trace_score = new_trace_a->getScore();
      if( getSearchLevel(level_num_a).useBeam()
	  && (trace_score > max_trace_scores_d(level_num_a))) {
	max_trace_scores_d(level_num_a) = trace_score;
      }
      
      // set the backpointer
      //
      new_trace_a->setBackPointer(old_trace_a);
      
      // add the trace to the next level's trace list
      //
      trace_lists_d(level_num_a).insertLast(new_trace_a);
      
      // print debugging information
      //
      if (debug_level_d >= Integral::DETAILED) {
	printNewPath(new_trace_a, old_trace_a);
      }
      
      // insert the trace in the trellis (optional)
      //
      if (search_mode_d == TRAIN) {
	if (!insertNewPath(old_trace_a, new_trace_a, weight)) {
	  return Error::handle(name(), L"addHypothesisPath", Error::ARG, __FILE__, __LINE__);
	}
      }      
    }

    // delete the trace since there is existing trace with the same history
    //    
    else {
      
      // print debugging information
      //      
      if (debug_level_d >= Integral::DETAILED) { 
	printDeletedPath(new_trace_a, old_trace_a);
      }
      
      // discard the new trace
      //
      delete new_trace_a;
      new_trace_a = (Trace*)NULL;
      
      // insert the trace in the trellis (optional)
      //      
      if (search_mode_d == TRAIN) {
	if (existing_trace != (Trace*)NULL) {
	  if (!insertOldPath(old_trace_a, existing_trace, weight)) {
	    return Error::handle(name(), L"addHypothesisPath", Error::ARG, __FILE__, __LINE__);
	  }
	}
	else {
	  return Error::handle(name(), L"addHypothesisPath", Error::ARG, __FILE__, __LINE__);
	}
      }
    }    
  }
  
  // propagate the path without pruning at all other levels
  //
  else {
    
    // update the maximal trace score for the beam pruning
    //
    float trace_score = new_trace_a->getScore();
    if( getSearchLevel(level_num_a).useBeam()
	&& (trace_score > max_trace_scores_d(level_num_a))) {
      max_trace_scores_d(level_num_a) = trace_score;
    }
    
    // set the backpointer
    //
    new_trace_a->setBackPointer(old_trace_a);
    
    // add the trace to the next level's trace list
    //
    trace_lists_d(level_num_a).insertLast(new_trace_a);
    
    // print debugging information
    //
    if (debug_level_d >= Integral::DETAILED) {
      printNewPath(new_trace_a, old_trace_a);
    }
    
    // insert the trace in the trellis (optional)
    //
    if (search_mode_d == TRAIN) {
      if (!insertNewPath(old_trace_a, new_trace_a, weight)) {
	return Error::handle(name(), L"addHypothesisPath", Error::ARG, __FILE__, __LINE__);
      }
    }    
  }

  // exit gracefully
  //
  return true;
}

// method: addHypothesisPath
//
// arguments:
//   Instance*& new_instance: (input) new input instance
//   Instance*& old_instance: (input) old input instance
//   long level_num: (input) current level number
//   long weight_a: (input) transition score
//
// return: logical error status
//
// this method add an instance to the hypothesis path
//
boolean HierarchicalSearch::addHypothesisPath(Instance*& new_instance_a,
					      Instance*& old_instance_a,
					      long level_num_a,
					      float weight_a) {

  // declare local variables
  //
  Context* symbol = (Context*)NULL;
  Instance* existing_instance = (Instance*)NULL;
  SearchNode* search_node = (SearchNode*)NULL;  
  GraphVertex<SearchNode>* v = (GraphVertex<SearchNode>*)NULL;

  // retrieve the symbol penalty and transition scale for this level
  //
  float tr_scale = getSearchLevel(level_num_a).getTrScale();
  float symbol_penalty = getSearchLevel(level_num_a).getSymbolPenalty();

  // retrieve the symbol, search node and central vertex for this instance
  //
  if ((symbol = new_instance_a->getSymbol()) == (Context*)NULL) {
    return false;
  }
  
  if ((v = symbol->getCentralVertex()) == (GraphVertex<SearchNode>*)NULL) {
    return false;
  }
  
  if ((search_node = v->getItem()) == (SearchNode*)NULL) {
    return false;
  }
  
  // add the transition score and symbol insertion penalty (when applicable)
  //
  long symbol_id = search_node->getSymbolId();
  
  if (!getSearchLevel(level_num_a).isDummySymbol(symbol_id) &&
      !getSearchLevel(level_num_a).isSPenaltyExcludeSymbol(symbol_id)) {
    new_instance_a->setScore(new_instance_a->getScore() +
			  (weight_a * tr_scale) + symbol_penalty);
  }
  else {
    new_instance_a->setScore(new_instance_a->getScore() + (weight_a * tr_scale));
  }
  
  // add any posterior score such as n-symbol probabilities
  //
  if (getSearchLevel(level_num_a).useNSymbol()) {
    
    if (!getSearchLevel(level_num_a).isDummySymbol(symbol_id) &&
	!getSearchLevel(level_num_a).isNSymbolExcludeSymbol(symbol_id) &&
	!v->isStart() && !v->isTerm()) {
      
      shiftNSymbol(new_instance_a, level_num_a, v);
      
      float posterior_score =
	getPosteriorScore(new_instance_a->getNSymbol(), level_num_a);
      
      new_instance_a->setScore(new_instance_a->getScore() + posterior_score);
    }
  }

  // add the instance to the search node's instance list
  //
  if (search_node->addInstance(new_instance_a, current_frame_d,
			       existing_instance)) {
    
    // update the maximal instance score for the beam pruning
    //
    float instance_score = new_instance_a->getScore();
    if( getSearchLevel(level_num_a).useBeam()
	&& (instance_score > max_instance_scores_d(level_num_a))) {
      max_instance_scores_d(level_num_a) = instance_score;
    }
    
    // set the backpointer
    //
    new_instance_a->setBackPointer(old_instance_a);
    
    // add the instance to the next level's instance list
    //
    instance_lists_d(level_num_a).insertLast(new_instance_a);

    // print debugging information
    //
    if (debug_level_d >= Integral::DETAILED) {
      printNewPath(new_instance_a, old_instance_a);
    }

    // insert the instance in the trellis
    //
    if (search_mode_d == TRAIN) {
      if (!insertNewPath(old_instance_a, new_instance_a, weight_a)) {
	return Error::handle(name(), L"addHypothesisPath", Error::ARG, __FILE__, __LINE__);
      }
    }      
  }

  else {

    // print debugging information
    //      
    if (debug_level_d >= Integral::DETAILED) { 
      printDeletedPath(new_instance_a, old_instance_a);
    }
      
    // delete the instance since there is existing instance - same history
    //
    delete new_instance_a;
    new_instance_a = (Instance*)NULL;
    
    if (search_mode_d == TRAIN) {
      if (existing_instance != (Instance*)NULL) {
	if (!insertOldPath(old_instance_a, existing_instance, weight_a)) {
	  return Error::handle(name(), L"addHypothesisPath", Error::ARG, __FILE__, __LINE__);
	}
      }
      else {
	return Error::handle(name(), L"addHypothesisPath", Error::ARG, __FILE__, __LINE__);
      }
    }      
  }

  // exit gracefully
  //
  return true;
}

// method: insertNewPath
//
// arguments:
//   Trace* parent: (input) source trace
//   Trace* child: (input) destination trace
//   float weight: (input) transition probability
//
// return: logical error status
//
// method adds a new path to the trellis
//
boolean HierarchicalSearch::insertNewPath(Trace* parent_a, Trace* child_a,
					  float weight_a) {

  // declare local variables
  //
  BiGraphVertex<TrainNode>* ptr = (BiGraphVertex<TrainNode>*)NULL;
  BiGraphVertex<TrainNode>* src = (BiGraphVertex<TrainNode>*)NULL;
  BiGraphVertex<TrainNode>* dst = (BiGraphVertex<TrainNode>*)NULL;  

  // make sure that neither the parent nor the child traces are null
  //
  if ((parent_a == (Trace*)NULL) || (child_a == (Trace*)NULL)) {
    return false;
  }

  // retrieve the refernece pointer
  //
  ptr = parent_a->getReference();
  
  // check to see if the parent trace exists in the map
  //
  if (ptr != (BiGraphVertex<TrainNode>*)NULL) {
    
    // get the parent and child vertices
    //
    src = ptr;
    dst = insertTrace(child_a);
    
    // add a transition in the trellis from the parent to the child
    //
    if (src != trellis_d.getTerm()) {
      trellis_d.insertArc(src, dst, false, weight_a);
    }
  } else {
    
    // get the parent and child vertices
    //    
    src = insertTrace(parent_a);
    dst = insertTrace(child_a);

    // add a transition in the trellis from the parent to the child
    //    
    if (src != trellis_d.getTerm()) {
      trellis_d.insertArc(src, dst, false, weight_a);
    }
  }

  // print debugging information
  //
  if (debug_level_d >= Integral::DETAILED) {
  
    GraphVertex<SearchNode>* src_vertex = src->getItem()->getReference()->getCentralVertex();
    GraphVertex<SearchNode>* dst_vertex = dst->getItem()->getReference()->getCentralVertex();    

    long src_frame = src->getItem()->getFrame();
    long dst_frame = dst->getItem()->getFrame();
  
    SearchSymbol src_sym;
    if (src_vertex->isStart()) {
      src_sym.assign(L"_START_");
    }
    else if (src_vertex->isTerm()) {
      src_sym.assign(L"_TERM_");
    }
    else {
      src->getItem()->getReference()->print(src_sym);
    }
   
    SearchSymbol dst_sym;
    if (dst_vertex->isStart()) {
      dst_sym.assign(L"_START_");
    }
    else if (dst_vertex->isTerm()) {
      dst_sym.assign(L"_TERM_");
    }
    else {
      dst->getItem()->getReference()->print(dst_sym);
    }
    
    String val;  
    String output(L"\n-> source: ");
    output.concat(src_vertex);
    output.concat(L", destination: ");
    output.concat(dst_vertex);    
    
    output.concat(L"\n   [");
    output.concat(src_sym);
    output.concat(L"], frame: ");
    val.assign((long)src_frame);  
    output.concat(val);
    output.concat(L" -> [");
    output.concat(dst_sym);
    output.concat(L"], frame: ");
    val.assign((long)dst_frame);  
    output.concat(val);
    Console::put(output);
  }
  
  // exit gracefully
  //
  return true;
}

// method: insertNewPath
//
// arguments:
//   Instance* parent: (input) source instance
//   Instance* child: (input) destination instance
//   float weight: (input) transition probability
//
// return: logical error status
//
// method adds a new path to the trellis
//
boolean HierarchicalSearch::insertNewPath(Instance* parent_a,Instance* child_a,
					  float weight_a) {

  // declare local variables
  //
  BiGraphVertex<TrainNode>* ptr = (BiGraphVertex<TrainNode>*)NULL;
  BiGraphVertex<TrainNode>* src = (BiGraphVertex<TrainNode>*)NULL;
  BiGraphVertex<TrainNode>* dst = (BiGraphVertex<TrainNode>*)NULL;  

  // make sure that the child instances is not null
  //
  if (child_a == (Instance*)NULL) {
    return false;
  }

  // when the parent is null we are at the begining of the utterance
  //
  if (parent_a == (Instance*)NULL) {

    // get the parent and child vertices
    //
    src = trellis_d.getStart();
    dst = insertInstance(child_a);
    
    // add a transition in the trellis from the parent to the child
    //
    if (src != trellis_d.getTerm()) {
      trellis_d.insertArc(src, dst, false, 0);
    }
    return true;
  }

  // retrieve the refernece pointer
  //
  ptr = parent_a->getReference();
  
  // check to see if the parent trace exists in the map
  //
  if (ptr != (BiGraphVertex<TrainNode>*)NULL) {
    
    // get the parent and child vertices
    //
    src = ptr;
    dst = insertInstance(child_a);
    
    // add a transition in the trellis from the parent to the child
    //
    if (src != trellis_d.getTerm()) {
      trellis_d.insertArc(src, dst, false, weight_a);
    }
  } else {
    
    // get the parent and child vertices
    //    
    src = insertInstance(parent_a);
    dst = insertInstance(child_a);

    // add a transition in the trellis from the parent to the child
    //    
    if (src != trellis_d.getTerm()) {
      trellis_d.insertArc(src, dst, false, weight_a);
    }
  }

  // print debugging information
  //
  if (debug_level_d >= Integral::DETAILED) {
  
    GraphVertex<SearchNode>* src_vertex = src->getItem()->getReference()->getCentralVertex();
    GraphVertex<SearchNode>* dst_vertex = dst->getItem()->getReference()->getCentralVertex();    

    long src_frame = src->getItem()->getFrame();
    long dst_frame = dst->getItem()->getFrame();
  
    SearchSymbol src_sym;
    if (src_vertex->isStart()) {
      src_sym.assign(L"_START_");
    }
    else if (src_vertex->isTerm()) {
      src_sym.assign(L"_TERM_");
    }
    else {
      src->getItem()->getReference()->print(src_sym);
    }
   
    SearchSymbol dst_sym;
    if (dst_vertex->isStart()) {
      dst_sym.assign(L"_START_");
    }
    else if (dst_vertex->isTerm()) {
      dst_sym.assign(L"_TERM_");
    }
    else {
      dst->getItem()->getReference()->print(dst_sym);
    }
    
    String val;  
    String output(L"\n-> source: ");
    output.concat(src_vertex);
    output.concat(L", destination: ");
    output.concat(dst_vertex);    
    
    output.concat(L"\n   [");
    output.concat(src_sym);
    output.concat(L"], frame: ");
    val.assign((long)src_frame);  
    output.concat(val);
    output.concat(L" -> [");
    output.concat(dst_sym);
    output.concat(L"], frame: ");
    val.assign((long)dst_frame);  
    output.concat(val);
    Console::put(output);
  }
  
  // exit gracefully
  //
  return true;
}

// method: insertOldPath
//
// arguments:
//   Trace* parent: (input) source trace
//   Trace* child: (input) destination trace
//   float weight: (input) transition probability
//
// return: logical error status
//
// method adds a new path to the trellis
//
boolean HierarchicalSearch::insertOldPath(Trace* parent_a, Trace* child_a,
					  float weight_a) {

  // declare local variables
  //
  BiGraphVertex<TrainNode>* ptr = (BiGraphVertex<TrainNode>*)NULL;  
  BiGraphVertex<TrainNode>* src = (BiGraphVertex<TrainNode>*)NULL;
  BiGraphVertex<TrainNode>* dst = (BiGraphVertex<TrainNode>*)NULL;  

  // make sure that neither the parent nor the child traces are null
  //
  if ((parent_a == (Trace*)NULL) || (child_a == (Trace*)NULL)) {
    return false;
  }  

  // check to see if the parent trace exists in the map
  //  
  ptr = parent_a->getReference();
  if (ptr != (BiGraphVertex<TrainNode>*)NULL) {
    src = ptr;
  }

  // check to see if the child trace exists in the map
  //  
  ptr = child_a->getReference();
  if (ptr != (BiGraphVertex<TrainNode>*)NULL) {
    dst = ptr;
  }

  // add a transition in the trellis from the parent to the child
  //  
  if ((src != (BiGraphVertex<TrainNode>*)NULL) &&
      (dst != (BiGraphVertex<TrainNode>*)NULL)) {

    trellis_d.insertArc(src, dst, false, weight_a);

    // print debugging information
    //
    if (debug_level_d >= Integral::DETAILED) {
      
      GraphVertex<SearchNode>* src_vertex = src->getItem()->getReference()->getCentralVertex();
      GraphVertex<SearchNode>* dst_vertex = dst->getItem()->getReference()->getCentralVertex();    
      
      long src_frame = src->getItem()->getFrame();
      long dst_frame = dst->getItem()->getFrame();
      
      SearchSymbol src_sym;
      if (src_vertex->isStart()) {
	src_sym.assign(L"_START_");
      }
      else if (src_vertex->isTerm()) {
	src_sym.assign(L"_TERM_");
      }
      else {
	src->getItem()->getReference()->print(src_sym);
      }
      
      SearchSymbol dst_sym;
      if (dst_vertex->isStart()) {
	dst_sym.assign(L"_START_");
      }
      else if (dst_vertex->isTerm()) {
	dst_sym.assign(L"_TERM_");
      }
      else {
	dst->getItem()->getReference()->print(dst_sym);
      }
      
      String val;  
      String output(L"\n-> source: ");
      output.concat(src_vertex);
      output.concat(L", destination: ");
      output.concat(dst_vertex);    
      
      output.concat(L"\n   [");
      output.concat(src_sym);
      output.concat(L"], frame: ");
      val.assign((long)src_frame);  
      output.concat(val);
      output.concat(L" -> [");
      output.concat(dst_sym);
      output.concat(L"], frame: ");
      val.assign((long)dst_frame);  
      output.concat(val);
      Console::put(output);
    }    
  }  
  
  // exit gracefully
  //
  return true;
}

// method: insertOldPath
//
// arguments:
//   Instance* parent: (input) source instance
//   Instance* child: (input) destination instance
//   float weight: (input) transition probability
//
// return: logical error status
//
// method adds a new path to the trellis
//
boolean HierarchicalSearch::insertOldPath(Instance* parent_a,
					  Instance* child_a,
					  float weight_a) {

  // declare local variables
  //
  BiGraphVertex<TrainNode>* ptr = (BiGraphVertex<TrainNode>*)NULL;
  BiGraphVertex<TrainNode>* src = (BiGraphVertex<TrainNode>*)NULL;
  BiGraphVertex<TrainNode>* dst = (BiGraphVertex<TrainNode>*)NULL;  

  // make sure that neither the parent nor the child instances are null
  //
  if ((parent_a == (Instance*)NULL) || (child_a == (Instance*)NULL)) {
    return false;
  }  

  // check to see if the parent instance exists in the map
  //
  ptr = parent_a->getReference();
  if (ptr != (BiGraphVertex<TrainNode>*)NULL) {  
    src = ptr;
  }

  // check to see if the child instance exists in the map
  //
  ptr = child_a->getReference();
  if (ptr != (BiGraphVertex<TrainNode>*)NULL) {  
    dst = ptr;
  }

  // add a transition in the trellis from the parent to the child
  //  
  if ((src != (BiGraphVertex<TrainNode>*)NULL) &&
      (dst != (BiGraphVertex<TrainNode>*)NULL)) {

    trellis_d.insertArc(src, dst, false, weight_a);

    // print debugging information
    //
    if (debug_level_d >= Integral::DETAILED) {
      
      GraphVertex<SearchNode>* src_vertex = src->getItem()->getReference()->getCentralVertex();
      GraphVertex<SearchNode>* dst_vertex = dst->getItem()->getReference()->getCentralVertex();    
      
      long src_frame = src->getItem()->getFrame();
      long dst_frame = dst->getItem()->getFrame();
      
      SearchSymbol src_sym;
      if (src_vertex->isStart()) {
	src_sym.assign(L"_START_");
      }
      else if (src_vertex->isTerm()) {
	src_sym.assign(L"_TERM_");
      }
      else {
	src->getItem()->getReference()->print(src_sym);
      }
      
      SearchSymbol dst_sym;
      if (dst_vertex->isStart()) {
	dst_sym.assign(L"_START_");
      }
      else if (dst_vertex->isTerm()) {
	dst_sym.assign(L"_TERM_");
      }
      else {
	dst->getItem()->getReference()->print(dst_sym);
      }
      
      String val;  
      String output(L"\n-> source: ");
      output.concat(src_vertex);
      output.concat(L", destination: ");
      output.concat(dst_vertex);    
      
      output.concat(L"\n   [");
      output.concat(src_sym);
      output.concat(L"], frame: ");
      val.assign((long)src_frame);  
      output.concat(val);
      output.concat(L" -> [");
      output.concat(dst_sym);
      output.concat(L"], frame: ");
      val.assign((long)dst_frame);  
      output.concat(val);
      Console::put(output);
    }        
  }  
  
  // exit gracefully
  //
  return true;
}

// method: insertTrace
//
// arguments:
//  Trace* trace: (input) trace to be added to the trellis
//
// return: train node vertex
//
// insert the trace into the trellis
//
BiGraphVertex<TrainNode>* HierarchicalSearch::insertTrace(Trace* trace_a) {

  // declare local variables
  //
  TrainNode train_node;    
  BiGraphVertex<TrainNode>* vertex = (BiGraphVertex<TrainNode>*)NULL;

  // initialize the time stamp (t)
  //
  if (trace_a != (Trace*)NULL) {
    train_node.setFrame((long)trace_a->getFrame());
  }
  
  // initialize the forward/backward probabilities at time (t)
  //
  train_node.setAlpha(Integral::DB_LOG_MIN_VALUE);  
  train_node.setBeta(Integral::DB_LOG_MIN_VALUE);

  // initialize the reference pointers at time (t)
  //  
  if (trace_a != (Trace*)NULL) {
    train_node.setReference(trace_a->getSymbol());
  }
	  
  // initialize the statistical model at time (t)
  //
  if (trace_a != (Trace*)NULL) {
    train_node.setStatisticalModel(trace_a->getSymbol()->getCentralVertex()->
				   getItem()->getStatisticalModel());    
  }

  // insert the node in the trellis and return the graph vertex
  //
  vertex = trellis_d.insertVertex(&train_node);

  // initialize the reference pointers for the trace
  //  
  if (trace_a != (Trace*)NULL) {
    trace_a->setReference(vertex);
  }

  // return the inserted vertex
  //
  return vertex;
}

// method: insertInstance
//
// arguments:
//  Instance* instance: (input) instance to be added to the trellis
//
// return: train node vertex
//
// insert the instance into the trellis
//
BiGraphVertex<TrainNode>* HierarchicalSearch::insertInstance(Instance* instance_a) {

  // declare local variables
  //
  TrainNode train_node;    
  BiGraphVertex<TrainNode>* vertex = (BiGraphVertex<TrainNode>*)NULL;

  // initialize the time stamp (t)
  //
  if (instance_a != (Instance*)NULL) {
    train_node.setFrame((long)instance_a->getFrame());
  }
  
  // initialize the forward/backward probabilities at time (t)
  //
  train_node.setAlpha(Integral::DB_LOG_MIN_VALUE);  
  train_node.setBeta(Integral::DB_LOG_MIN_VALUE);

  // initialize the reference pointer at time (t)
  //
  if (instance_a != (Instance*)NULL) {  
    train_node.setReference(instance_a->getSymbol());
  }
	  
  // initialize the statistical model at time (t)
  //
  if (instance_a != (Instance*)NULL) {
    train_node.setStatisticalModel(instance_a->getSymbol()->
				   getCentralVertex()->
				   getItem()->getStatisticalModel());
  }

  // insert the node in the trellis and return the graph vertex
  //
  vertex = trellis_d.insertVertex(&train_node);
  
  // initialize the reference pointers for the instance
  //  
  if (instance_a != (Instance*)NULL) {
    instance_a->setReference(vertex);
  }

  // return the inserted vertex
  //
  return vertex;
}

// method: computeForwardBackward
//
// arguments:
//   Vector<VectorFloat> data: (output) feature vectors
//   float beta_threshold: (input) beta pruning threshold
//
// return: logical error status
//
// traverse the trellis and compute the state occupancies
//
BiGraph<TrainNode>* HierarchicalSearch::computeForwardBackward(Vector<VectorFloat>& data_a, float beta_threshold_a) {

  // setup the initial conditions for the forward backward algorithm
  //
  initializeForwardBackward();

  // traverse the trellis backwards and compute the backward probability
  //
  computeBackward(data_a, beta_threshold_a);

  // traverse the trellis backwards and compute the forward probability
  //
  computeForward(data_a);

  // return the trellis
  //
  return &trellis_d;  
}

// method: initializeForwardBackward
//
// arguments: none
//
// return: logical error status
//
// this method setup the initial conditions for the forward backward algorithm
//
boolean HierarchicalSearch::initializeForwardBackward() {

  // declare local variables
  //
  BiGraphVertex<TrainNode>* vertex = (BiGraphVertex<TrainNode>*)NULL;  

  // get the start node of the trellis
  //
  vertex = trellis_d.getStart();
  if (vertex != (BiGraphVertex<TrainNode>*)NULL) {
    vertex->getItem()->setAlpha(0);
  }
  
  // get the term node of the trellis
  //
  vertex = trellis_d.getTerm();
  if (vertex != (BiGraphVertex<TrainNode>*)NULL) {  
    vertex->getItem()->setBeta(0);
  }
  
  // exit gracefully
  //
  return true;      
}

// method: computeForward
//
// arguments:
//   Vector<VectorFloat> data: (output) feature vectors
//
// return: logical error status
//
// this method computes the forward probability of the network
//
boolean HierarchicalSearch::computeForward(Vector<VectorFloat>& data_a) {
  
  // iterate over all valid traces in the hypotheses list
  //
  BiGraphVertex<TrainNode>* child = (BiGraphVertex<TrainNode>*)NULL;
  BiGraphVertex<TrainNode>* vertex = (BiGraphVertex<TrainNode>*)NULL;
  
  DoubleLinkedList<BiGraphVertex<TrainNode> > active_list(DstrBase::USER);  
 
  // add the term vertex to the active list
  //
  active_list.insertLast(trellis_d.getStart());
  
  // loop until the active list is empty
  //
  while (!active_list.isEmpty()) {

    // remove the first element from the list
    //
    active_list.removeFirst(vertex);

    // compute the backward probability of the vertex
    //
    computeAlpha(data_a, vertex);

    // insert the children of the vertex on the list
    //
    for (boolean more = vertex->gotoFirstChild();
	 more; more = vertex->gotoNextChild()) {

      child = vertex->getCurrChild()->getVertex();

      if (child != (BiGraphVertex<TrainNode>*)NULL) {
	if ((child->getItem() != (TrainNode*)NULL) &&
	    !child->getItem()->isAlphaValid()) {
	  active_list.insertLast(child);
	  child->getItem()->setAlphaValid(true);
	}
      }
    }
  }

  // clear the list before exiting
  //
  active_list.clear();
  
  // exit gracefully
  //
  return true;
}

// method: computeBackward
//
// arguments:
//   Vector<VectorFloat> data: (input) feature vectors
//   float beta_threshold: (input) beta threshold
//
// return: logical error status
//
// this method computes the backward probability of the network
//
boolean HierarchicalSearch::computeBackward(Vector<VectorFloat>& data_a,
					    float beta_threshold_a) {
  
  // iterate over all valid traces in the hypotheses list
  //
  long timestamp = 0;
  float beta_value = 0.0;
  
  BiGraphVertex<TrainNode>* parent = (BiGraphVertex<TrainNode>*)NULL;
  BiGraphVertex<TrainNode>* vertex = (BiGraphVertex<TrainNode>*)NULL;

  VectorFloat beta_bound;    
  DoubleLinkedList<BiGraphVertex<TrainNode> > active_list(DstrBase::USER);  


  long num_frames = data_a.length();
  long num_models = getSearchLevel(getNumLevels() - 1).getStatisticalModels().length();
  
  float** model_cache = new float*[num_frames];
  for (long i=0; i < num_frames; i++) {

    model_cache[i] = new float[num_models];

    for (long j=0; j < num_models; j++) {
      model_cache[i][j] = Integral::DB_LOG_MIN_VALUE;
    }
  }
  
  // set the length and initialize the beta bound
  //
  beta_bound.setLength(data_a.length());
  beta_bound.assign(Integral::DB_LOG_MIN_VALUE);

  // first pass determines the maximum beta score for each time frame
  //
  
  // add the term vertex to the active list
  //
  active_list.insertLast(trellis_d.getTerm());
  
  // loop until the active list is empty
  //
  while (!active_list.isEmpty()) {

    // remove the first element from the list
    //
    active_list.removeFirst(vertex);

    // compute the backward probability of the vertex
    //
    computeBeta(data_a, vertex, model_cache);

    // set the maximum beta value for the time frame if applicable
    //
    if (vertex->getItem() != (TrainNode*)NULL) {

      vertex->getItem()->setBetaValid(false);
      timestamp = vertex->getItem()->getFrame();
      
      if ((timestamp >= 0) && (timestamp < data_a.length())) {

	beta_value = vertex->getItem()->getBeta();

	if (beta_bound(timestamp) < beta_value) {
	  beta_bound(timestamp) = beta_value;
	}
      }
    }
    
    // insert the parents of the vertex on the list
    //
    for (boolean more = vertex->gotoFirstParent();
	 more; more = vertex->gotoNextParent()) {

      parent = vertex->getCurrParent()->getVertex();
	  
      if (parent != (BiGraphVertex<TrainNode>*)NULL) {
	if ((parent->getItem() != (TrainNode*)NULL) &&
	    !parent->getItem()->isBetaValid()) {
	  active_list.insertLast(parent);
	  parent->getItem()->setBetaValid(true);
	}
      }
    }
  }

  // second pass applies beta pruning using the maximum beta score
  //
  
  // add the term vertex to the active list
  //
  active_list.insertLast(trellis_d.getTerm());
  
  // loop until the active list is empty
  //
  while (!active_list.isEmpty()) {

    // remove the first element from the list
    //
    active_list.removeFirst(vertex);

    // set the maximum beta value for the time frame if applicable
    //
    if (vertex->getItem() != (TrainNode*)NULL) {

      timestamp = vertex->getItem()->getFrame();

      if ((timestamp >= 0) && (timestamp < data_a.length())) {

	beta_value = vertex->getItem()->getBeta();

	if ((beta_bound(timestamp) - beta_value) > beta_threshold_a) {
	  vertex->getItem()->setValidNode(false);
	  vertex->getItem()->setBeta(Integral::DB_LOG_MIN_VALUE);
	}
      }
    }
    
    // insert the parents of the vertex on the list
    //
    for (boolean more = vertex->gotoFirstParent();
	 more; more = vertex->gotoNextParent()) {

      parent = vertex->getCurrParent()->getVertex();
	  
      if (parent != (BiGraphVertex<TrainNode>*)NULL) {
	if ((parent->getItem() != (TrainNode*)NULL) &&
	    !parent->getItem()->isBetaValid()) {
	  active_list.insertLast(parent);
	  parent->getItem()->setBetaValid(true);
	}
      }
    }
  }

  // clear the data structures before exiting
  //
  active_list.clear();  

  // free memory
  //
  for (long i=0; i < num_frames; i++) {
    delete [] model_cache[i];
    model_cache[i] = (float*)NULL;
  }
  
  delete [] model_cache;
  model_cache = (float**)NULL;
  
  // exit gracefully
  //
  return true;
}

// method: computeAlpha
//
// arguments:
//   Vector<VectorFloat> data: (output) feature vectors
//  BiGraphVertex<TrainNode>& vertex: (input) trace whose back pointers we need
//                                     to recursively visit
//  float weight: (input) weight of the arc from the current trace to the next
//
// [1] L. Rabiner, B. H. Juang, "Fundamentals of Speech Recognition", Prentice
// Hall P T R, New Jersey, 1993, pp. 337-338, ISBN 0-13-015157-2
//
// 1. Initialization:
//
//    Alpha(1)[i] = pi(i) * b(1)           1 <= i <= N
//         pi(i) = initial probability for state i
//
// 2. Induction:
//
//    Alpha(t + 1)[j] = aij * b(t + 1)[j] * Alpha (t)[i]
//             i = [1, ..., N]
//             b = model evaluation for state j at time t + 1
//             a = state transition between i at time t and j at time t + 1
//
// return: logical error status
//
// computes the forward probability
//
boolean HierarchicalSearch::computeAlpha(Vector<VectorFloat>& data_a,
					 BiGraphVertex<TrainNode>* vertex_a) {

  // declare local variables
  //
  double tmp_accum = 0.0;
  double accumulator = 0.0;
  boolean accum_valid = false;
  
  TrainNode* prev_node = (TrainNode*)NULL;
  TrainNode* train_node = (TrainNode*)NULL;  
  
  // get the state to be evaluated
  //
  train_node = vertex_a->getItem();

  // make sure the state is not null
  //
  if (train_node == (TrainNode*)NULL) {
    return true;    
  }
  
  // loop over all states at the previous time frame
  //
  accumulator = Integral::DB_LOG_MIN_VALUE;
  for (boolean more = vertex_a->gotoFirstParent();
       more; more = vertex_a->gotoNextParent()) {
          
    // get the train node associate with the state at the previous time frame
    //
    prev_node = vertex_a->getCurrParent()->getVertex()->getItem();

    // make sure that this node contributes to the alpha probability
    //
    if ((prev_node == (TrainNode*)NULL) || !prev_node->getValidNode()) {
      continue;
    }

    // compute the forward probability
    //
    tmp_accum = prev_node->getAlpha() +
      vertex_a->getCurrParent()->getWeight();
    
    // accumulate the backward probability over different states
    //
    accum_valid = true;
    accumulator = Integral::logAddLog(accumulator, tmp_accum);
  }
    
  // when the current state does not have a valid statistical model
  // propagate the forward probabilities through
  //
  if (!train_node->getValidModel()) {

    if (accum_valid) {
      train_node->setAlpha(accumulator);
    }
  }
  
  // when the current state does have a valid statistical model propagate
  // the forward probabilities through after model evaluation
  //
  else {

    if (accum_valid) {
      accumulator += train_node->getScore();
      train_node->setAlpha(accumulator);
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: computeBeta
//
// arguments:
//   Vector<VectorFloat> data: (output) feature vectors
//  BiGraphVertex<TrainNode>& vertex: (input) trace whose back pointers we need
//                                     to recursively visit
//  float** model_cache: (input) model cache
//
// [1] L. Rabiner, B. H. Juang, "Fundamentals of Speech Recognition", Prentice
// Hall P T R, New Jersey, 1993, pp. 337-338, ISBN 0-13-015157-2
//
// 1. Initialization:
//
//    Beta(T)[i] = 1           1 <= i <= N
//
// 2. Induction:
//
//    Beta(t)[i] = aij * b(t + 1)[j] * Beta (t + 1)[j]
//             j = [1, ..., N]
//             b = model evaluation for state j at time t + 1
//             a = state transition between i at time t and j at time t + 1
//
// return: logical error status
//
// computes the backward probability
//
boolean HierarchicalSearch::computeBeta(Vector<VectorFloat>& data_a,
					BiGraphVertex<TrainNode>* vertex_a,
					float** model_cache_a) {

  // declare local variables
  //
  double tmp_accum = 0.0;
  double accumulator = 0.0;
  boolean accum_valid = false;
  
  TrainNode* next_node = (TrainNode*)NULL;
  TrainNode* train_node = (TrainNode*)NULL;  
  SearchNode* search_node = (SearchNode*)NULL;
  
  Context* reference_symbol = (Context*)NULL;
  GraphVertex<SearchNode>* reference_vertex = (GraphVertex<SearchNode>*)NULL;
  
  // get the trace associated with the state
  //
  train_node = vertex_a->getItem();

  // make sure the state is not null
  //
  if (train_node == (TrainNode*)NULL) {
    return true;    
  }

  // loop over all states at next time frame
  //
  accumulator = Integral::DB_LOG_MIN_VALUE;
  for (boolean more = vertex_a->gotoFirstChild();
       more; more = vertex_a->gotoNextChild()) {

    // get the train node associate with the state at the next time frame
    //
    next_node = vertex_a->getCurrChild()->getVertex()->getItem();

    // make sure that this node contributes to the beta probability
    //
    if ((next_node == (TrainNode*)NULL) || !next_node->getValidNode()) {
      continue;
    }

    // compute the backward probability
    //
    float score = 0.0;    
    long frame = next_node->getFrame();

    reference_vertex = (GraphVertex<SearchNode>*)NULL;
    if ((reference_symbol = next_node->getReference()) != (Context*)NULL) {
      reference_vertex = reference_symbol->getCentralVertex();
    }

    if ((reference_vertex != (GraphVertex<SearchNode>*)NULL) &&
	((search_node = reference_vertex->getItem()) != (SearchNode*)NULL)) {
      
      if ((frame > -1) && (frame < data_a.length())) {
	
	StatisticalModel* stat_model = search_node->getStatisticalModel();
	if (stat_model != (StatisticalModel*)NULL) {

	  long model_index = search_node->getModelId();
	  
	  if (model_cache_a[frame][model_index] != Integral::DB_LOG_MIN_VALUE) {
	    score = model_cache_a[frame][model_index];
	  }
	  else {

	    score = stat_model->getLogLikelihood(data_a(frame));
	    model_cache_a[frame][model_index] = score;
	  }
	}
      }
    }
    
    // update the score
    //
    next_node->setScore(score);
    
    tmp_accum = next_node->getBeta() +
      vertex_a->getCurrChild()->getWeight() + score;
      
    // accumulate the backward probability over different states
    //
    accum_valid = true;
    accumulator = Integral::logAddLog(accumulator, tmp_accum);
  }

  // mark the train node so that is used in the accumulate/update phase
  //
  train_node->setValidNode(true);
    
  // have we accumulated any statistics?
  //
  if (accum_valid) {
    train_node->setBeta(accumulator);
  }
  
  // exit gracefully
  //
  return true;
}

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

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

  // now we evaluate all traces that are in the lowest level and move 
  // them forward one arc. viterbi pruning takes place at this level 
  // as does beam pruning.
  //
  status = (evaluateTraceModels() || status);

  // exit gracefully
  //
  return status;
}

// method: traverseInstanceLevels
//
// arguments: none
//
// return: logical error status
//
// do a single-step traversal of the hypotheses at each level
//
boolean HierarchicalSearch::traverseInstanceLevels() {
  
  // 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 = (propagateInstances() || 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.
  //
  status = (evaluateInstanceModels() || status);

  // exit gracefully
  //
  return status;
}

// method: propagateInstances
//
// arguments: none
//
// return: logical error status
//
// move instances through the hierarchy until all active instances are ready
// to be evaluated
//
boolean HierarchicalSearch::propagateInstances() {

  // define local variables
  //
  long level_num = 0;
  boolean status = true;
  long num_levels = getNumLevels();

  // clear the hypothesis list since we are generating new hypotheses for this
  // frame
  //
  clearValidHypsInstance();

  // move all traces forward in the search space. we do this by
  // pushing traces 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 traces 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 traces happened
    //
    status = false;

    for (level_num = (long)initial_level_d;  level_num < num_levels; level_num++) {
      if (getSearchLevel(level_num).useBeam()) {
	max_instance_scores_d(level_num) = Trace::INACTIVE_SCORE;
      }
    }
    
    // work from the bottom up until we reach a point where all traces
    // are ready to descend again
    //
    for (level_num = num_levels - 1; level_num >= (long)initial_level_d; 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);
      }
      
      status = (propagateInstancesUp(level_num) || status);
    }
    
    // propagate traces down the hierarchy, carrying out viterbi pruning
    //
    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 instance up");
	}

	beamPruneInstance(level_num);
      }

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

	if (debug_level_d >= Integral::DETAILED) {
	  Console::put(L"\ninstance pruning after propagation instances up");
	}
	
	instancePruneInstance(level_num);
      }
      
      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);
      }
      
      status = (propagateInstancesDown(level_num) || status);
    }
  }

  // print debugging information
  //
  if (debug_level_d >= Integral::ALL) {
    
    String val;
    val.assign(L"old 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: propagateTraces
//
// arguments: none
//
// return: logical error status
//
// move traces through the hierarchy until all active traces are ready to be
// evaluated
//
boolean HierarchicalSearch::propagateTraces() {
  
  // 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
  //
  clearValidHypsTrace();

  // move all traces forward in the search space. we do this by
  // pushing traces 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 traces should be in the lowest
  // level and should be ready for evaluation.
  //
  while ((getActiveTraces() != getActiveTraces(num_levels - 1)) ||
	 status) {
    
    // status indicates whether any movement in the traces happened
    //
    status = false;

    for (level_num = (long)initial_level_d;  level_num < num_levels; level_num++) {
      if (getSearchLevel(level_num).useBeam()) {
	max_trace_scores_d(level_num) = Trace::INACTIVE_SCORE;
      }
    }
    
    // work from the bottom up until we reach a point where all traces
    // 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 >= (long)initial_level_d; level_num--) {
      
      if (debug_level_d >= Integral::DETAILED) {      
	String output;        
	output.assign(L"propagating traces up from level: ");
	output.concat(level_num);
	output.concat(L" in frame: ");    
	output.concat(current_frame_d);
	Console::put(output);
      }
      
      // propagate up
      //
      status = (propagateTracesUp(level_num) || status);
    }
    
    // propagate traces 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 trace up");
	}

	beamPruneTrace(level_num);
      }

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

	if (debug_level_d >= Integral::DETAILED) {
	  Console::put(L"\ninstance pruning after propagation trace up");
	}
	
	instancePruneTrace(level_num);
      }
      
      if (debug_level_d >= Integral::DETAILED) {
	String output;        
	output.assign(L"propagating traces down from level: ");
	output.concat(level_num);
	output.concat(L" in frame: ");    
	output.concat(current_frame_d);
	Console::put(output);
      }
      
      // propagate down
      //
      status = (propagateTracesDown(level_num) || status);
    }
  }
  
  // exit gracefully
  //
  return true;
}

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

  // define local variables
  //
  String out;
  String val;
  long level_num = getNumLevels() - 1;
  boolean end_loop = false;
  SearchSymbol item;
  
  Instance* curr_instance = (Instance*)NULL;
  Instance* next_instance = (Instance*)NULL;
  
  GraphVertex<SearchNode>* curr_vert = (GraphVertex<SearchNode>*)NULL;
  DiGraph<SearchNode>* parent_graph = (DiGraph<SearchNode>*)NULL;   
  BiGraphVertex<TrainNode>* vertex = (BiGraphVertex<TrainNode>*)NULL;
  
  // 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);
  }

  // when we are NOT in context generation mode -
  //   evaluate each instance at the lowest level of the hierarchy
  //
  if (!context_generation_mode_d && (search_mode_d != TRAIN)) {

    // 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 model_score =
	curr_vert->getItem()->evaluateData(features_d, (long)current_frame_d);
      float new_score = curr_instance->getScore() + model_score;
      curr_instance->setScore(new_score);

      if (search_mode_d == TRAIN) {
	
	// update the trained node in the trellis
	//
	vertex = curr_instance->getReference();
	
	if (vertex == (BiGraphVertex<TrainNode>*)NULL) {
	  return Error::handle(name(), L"evaluateTraceModels", Error::ARG, __FILE__, __LINE__);
	}
	vertex->getItem()->setScore(model_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 the model evaluation");
      }
      
      beamPruneInstance(getNumLevels() - 1);
    }

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

      if (debug_level_d >= Integral::DETAILED) {
	Console::put(L"\ninstance pruning after the model evaluation");
      }
      
      instancePruneInstance(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();
    parent_graph = curr_vert->getParentGraph();

    // when we are in context generation mode -
    //   we do not need to evaluate the states at the lowest level
    //
    if (context_generation_mode_d) {

      // create a new instance
      //
      next_instance = new Instance(*curr_instance);
      next_instance->setFrame((ulong)current_frame_d);

      // update the history with the next node at this level
      //
      Context term_context;
      term_context.assignAndAdvance((ulong)parent_graph->getTerm());      
      next_instance->setSymbol(context_pool_d.get(term_context));

      // set the backpointer
      //
      next_instance->setBackPointer(curr_instance);
      
      // add the instance to this level's instance list
      //
      instance_lists_d(level_num).insertLast(next_instance);
      
      if (debug_level_d >= Integral::DETAILED) { 
	printNewPath(next_instance, curr_instance);
      }
      
      // 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();

      // evaluate the next instance in the list
      //
      continue;
    }
    
    // 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);
	
	// update the history with the next node at this level
	//
	GraphArc<SearchNode>* tmp_arc = curr_vert->getCurr();
	
	next_instance->setSymbol(context_pool_d.shiftAndAllocate(next_instance->getSymbol(), tmp_arc->getVertex()));
	
	// add the instance to the search node's instance list
	//
	if (!addHypothesisPath(next_instance, curr_instance, level_num,
			       tmp_arc->getWeight())) {
	  return Error::handle(name(), L"evaluateInstanceModels", 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: evaluateTraceModels
//
// arguments: (none)
//
// return: logical error status.
//
// evaluate all statistical models
//
boolean HierarchicalSearch::evaluateTraceModels() {

  // define local variables
  //
  String out;
  String val;
  long level_num = getNumLevels() - 1;
  boolean end_loop = false;
  Trace* curr_trace = (Trace*)NULL;
  Trace* next_trace = (Trace*)NULL;    
  GraphVertex<SearchNode>* curr_vert = (GraphVertex<SearchNode>*)NULL;
  SearchSymbol item;
  
  BiGraphVertex<TrainNode>* vertex = (BiGraphVertex<TrainNode>*)NULL;
  
  // 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);
  }

  //  add for word-internal context generation
  // when we are NOT in context generation mode -
  //   evaluate each instance at the lowest level of the hierarchy
  //
  if (!context_generation_mode_d && (search_mode_d != TRAIN)) {  

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

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

      // evaluate the statistical model
      //
      float model_score =
	curr_vert->getItem()->evaluateData(features_d, (long)current_frame_d);
      float new_score = curr_trace->getScore() + model_score;
      curr_trace->setScore(new_score);

      if (search_mode_d == TRAIN) {
      
	// update the trained node in the trellis
	//
	vertex = curr_trace->getReference();

	if (vertex == (BiGraphVertex<TrainNode>*)NULL) {
	  return Error::handle(name(), L"evaluateTraceModels", Error::ARG, __FILE__, __LINE__);
	}
	vertex->getItem()->setScore(model_score);
      }
    
      // update the maximal trace score for the beam pruning
      //
      if(getSearchLevel(level_num).useBeam()
	 && (new_score > max_trace_scores_d(level_num))) {
	max_trace_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 the model evaluation");
      }
    
      beamPruneTrace(getNumLevels() - 1);
    }

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

      if (debug_level_d >= Integral::DETAILED) {
	Console::put(L"\ninstance pruning after the model evaluation");
      }
    
      instancePruneTrace(getNumLevels() - 1);
    }
  } // end of non-context-generation mode
  
  // update current frame
  //        - traces generated later will correspond to the next frame
  //
  current_frame_d += 1 ;

  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 traces
  // forward that were just evaluated
  //
  trace_lists_d(level_num).gotoLast();
  trace_lists_d(level_num).setMark();
  trace_lists_d(level_num).gotoFirst();

  if (debug_level_d >= Integral::DETAILED) {
    Console::put(L"\npropagating traces forward after the model evaluation");
  }
  
  // loop until we have propagated all evaluated traces forward
  //
  end_loop = (trace_lists_d(level_num).length() < 1);
  while (!end_loop) {
    
    end_loop = trace_lists_d(level_num).isMarkedElement();
    
    // get the current vertex in the search graph
    //
    curr_trace = trace_lists_d(level_num).getCurr();
    curr_vert = curr_trace->getSymbol()->getLastVertex();

    //  add for word-internal context generation
    //  we do not need to evaluate the states at the lowest level
    //
    if (context_generation_mode_d) {

      // get parent graph
      //
      DiGraph<SearchNode>* parent_graph = curr_vert->getParentGraph();

      // create a new trace
      //
      next_trace = new Trace(*curr_trace);
      next_trace->setFrame((ulong)current_frame_d);

      // update the history with the next node at this level
      //
      next_trace->getSymbol()->assignAndAdvance((ulong)parent_graph->getTerm());

      // set the backpointer
      //
      next_trace->setBackPointer(curr_trace);
      
      // add the trace to this level's trace list
      //
      trace_lists_d(level_num).insertLast(next_trace);
      
      if (debug_level_d >= Integral::DETAILED) { 
	printNewPath(next_trace, curr_trace);
      }
      
      // bump the current trace off of the trace list
      //
      trace_lists_d(level_num).removeFirst(curr_trace);
      if (curr_trace->getRefCount() < 1) {
	Trace::deleteTrace(curr_trace, true);
      }
      trace_lists_d(level_num).gotoFirst();

      // evaluate the next trace in the list
      //
      continue;
    }
    
    // if this trace is inactive, then delete it
    //
    if (!curr_trace->isActive()) {
      
      // bump the current trace off of the trace list
      //
      if (debug_level_d >= Integral::ALL) {
	Console::put(L" not active");
      }      
      trace_lists_d(level_num).removeFirst(curr_trace);
      Trace::deleteTrace(curr_trace, 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 trace
	//
	next_trace = new Trace(*curr_trace);
	next_trace->setFrame((ulong)current_frame_d);

	// has the context been previously generated? if so reuse the context,
	// if not generate a new context and add it to the context pool
	//
	GraphArc<SearchNode>* tmp_arc = curr_vert->getCurr();
	
	next_trace->setSymbol(context_pool_d.shiftAndAllocate(next_trace->getSymbol(), tmp_arc->getVertex()));
	
	// add the trace to the search node's trace list
	//
	if (!addHypothesisPath(next_trace, curr_trace, tmp_arc, level_num)) {
	  return Error::handle(name(), L"evaluateTraceModels", Error::ARG, __FILE__, __LINE__);
	}	
      }
      
      // bump the current trace off of the trace list
      //
      trace_lists_d(level_num).removeFirst(curr_trace);
      if (curr_trace->getRefCount() < 1) {
	Trace::deleteTrace(curr_trace, true);
      }
      trace_lists_d(level_num).gotoFirst();
      
    } // end if the trace is active
  }   // end while (!end_loop)
  
  // exit gracefully
  //
  return true;
}

// method: propagateInstancesUp
//
// 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::propagateInstancesUp(long level_num_a) {

  // define local variables
  //
  boolean status = false;
  boolean end_loop = false;

  Context* tmp_context = (Context*)NULL;  
  Instance* curr_instance = (Instance*)NULL;
  Instance* tmp_instance = (Instance*)NULL;
  Instance* next_instance = (Instance*)NULL;

  GraphVertex<SearchNode>* curr_vert = (GraphVertex<SearchNode>*)NULL;
    
  // 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()) {
      Instance::deleteInstance(curr_instance, true);
    }
    
    // else, extend paths and update scores
    //
    else {
      
      // get the current vertex in the search graph
      //
      curr_vert = curr_instance->getSymbol()->getCentralVertex();

      // if this vertex is a terminal vertex then ascend and create the traces
      // at the higher level
      //      
      if (curr_vert->isTerm()) {
	
	// set the status to true since we are propagating a instance
	//
	status = true;

	// if we are at the top level then put the instance into the valid
	// hypothesis list
	//
	if (level_num_a == (long)initial_level_d) {
	  instance_valid_hyps_d.insertLast(curr_instance);
	}
	
	// else move up one level and create the next set of instances
	//
	else {
	  
	  // create a new instance, 
	  //
	  next_instance = new Instance(*curr_instance);
	  next_instance->setFrame((ulong)current_frame_d);
	  next_instance->setBackPointer(curr_instance);

	  // decrement the history of the instance
	  //
	  next_instance->setHistory(history_pool_d.popAndAllocate(next_instance->getHistory(), tmp_context));

	  // the context must exist in the context pool in this case
	  //
	  next_instance->setSymbol(context_pool_d.get(*tmp_context));
	  
	  // print the debuging information
	  //	  
	  if (debug_level_d >= Integral::DETAILED) {
	    printNewPath(next_instance, curr_instance);
	  }

	  // insert the instance into the trellis
	  //
	  if (search_mode_d == TRAIN) {
	    if (!insertNewPath(curr_instance, next_instance, 0)) {
	      return Error::handle(name(), L"propagateInstancesUp", Error::ARG, __FILE__, __LINE__);
	    }
	  }
	  
	  if (isTerminal(next_instance, level_num_a - 1)) {

	    // create a temporary instance
	    //
	    tmp_instance = new Instance(*next_instance);
	    tmp_instance->setFrame((ulong)current_frame_d);
	    tmp_instance->setBackPointer(next_instance);
	    
	    // create a context with a terminal as the central context
	    //
	    Context term_context;
	    term_context.assignAndAdvance((ulong)(*h_digraph_d)((long)initial_level_d).getSubGraph(0).getTerm());
	      
	    tmp_instance->setSymbolStack(history_pool_d.pushAndAllocate(tmp_instance->getSymbolStack(), tmp_instance->getSymbol()));

	    tmp_instance->setSymbol(context_pool_d.get(term_context));
	    
	    // add the instance to this level's instance list
	    //
	    instance_lists_d(level_num_a - 1).insertLast(tmp_instance);

	    // print the debuging information
	    //
	    if (debug_level_d >= Integral::DETAILED) {
	      printNewPath(tmp_instance, next_instance);
	    }
	    
	    // insert the insatnce into the trellis
	    //
	    if (search_mode_d == TRAIN) {
	      if (!insertNewPath(next_instance, tmp_instance, 0)) {
		return Error::handle(name(), L"propagateInstancesUp", Error::ARG, __FILE__, __LINE__);
	      }
	    }		    
	  }
	  
	  // extend the current context
	  //
	  else {

	    if (!extendRightContexts(next_instance, level_num_a - 1)) {
	      return Error::handle(name(), L"propagateInstancesUp", Error::ARG, __FILE__, __LINE__);
	    }
	  }
	  
	  // delete next_instance if no instances propagated from it
	  // have survived
	  //
	  if (next_instance->getRefCount() < 1) {
	    Instance::deleteInstance(next_instance, false);
	  }
	}
	
	// bump the current instance off of the instance list
	//
	if (curr_instance->getRefCount() < 1) {
	  
	  // if this is at top level, we can not delete them since they
	  // are also referenced by the valid_hyps
	  //
	  if (level_num_a != (long)initial_level_d) {
	    Instance::deleteInstance(curr_instance, 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
      // evaluateInstanceModels function
      //
      else {
	
	// update the maximal instance score for the beam pruning
	//
	float instance_score = curr_instance->getScore();
	if(getSearchLevel(level_num_a).useBeam()
	  && (instance_score > max_instance_scores_d(level_num_a)) ) {
	  max_instance_scores_d(level_num_a) = instance_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: propagateTracesUp
//
// arguments:
//  long level_num: (input) the level for which we will propagate traces
//
// return: logical error status. if no propagations were made then return false
//
// propagate all traces up. only traces at the end of a subgraph are considered
//
boolean HierarchicalSearch::propagateTracesUp(long level_num_a) {

  // define local variables
  //
  boolean status = false;
  boolean end_loop = false;
  
  Trace* curr_trace = (Trace*)NULL;
  Trace* tmp_trace = (Trace*)NULL;
  Trace* next_trace = (Trace*)NULL;    
  Context* tmp_context = (Context*)NULL;
  
  GraphVertex<SearchNode>* curr_vert = (GraphVertex<SearchNode>*)NULL;
  GraphVertex<SearchNode>* tmp_vert = (GraphVertex<SearchNode>*)NULL;

  Context* symbol = (Context*)NULL;
  Trace* existing_trace = (Trace*)NULL;  
  SingleLinkedList<Trace> trace_list(DstrBase::USER);
  
  // make sure there is at least one trace to propagate
  //
  if (trace_lists_d(level_num_a).length() < 1) {
    return false;
  }
  
  // loop over all current traces and create the next level's traces. all new
  // traces will go at the end of the list so we set the mark to tell us when
  // to stop propagating
  //
  trace_lists_d(level_num_a).gotoLast();
  trace_lists_d(level_num_a).setMark();
  trace_lists_d(level_num_a).gotoFirst();
  
  while (!end_loop) {
    
    // loop until we reached the marked element
    //
    end_loop = trace_lists_d(level_num_a).isMarkedElement();
    
    // get the current first trace from the list
    //
    trace_lists_d(level_num_a).removeFirst(curr_trace);
    
    // if the trace is inactive then delete it and its backpath if necessary
    //
    if (!curr_trace->isActive()) {
      Trace::deleteTrace(curr_trace, true);
    }
    
    // else, extend paths and update scores
    //
    else {
      
      // get the current vertex in the search graph
      //
      curr_vert = curr_trace->getSymbol()->getCentralVertex();
      
      // if this vertex is a terminal vertex then ascend and create the traces
      // at the higher level
      //
      if (curr_vert->isTerm()) {
	
	// set the status to true since we are propagating a trace
	//
	status = true;

	// if we are at the top level then put the trace into the valid
	// hypothesis list
	//
	if (level_num_a == (long)initial_level_d) {

	  // insert the trace into the valid hypothesis list
	  //
	  trace_valid_hyps_d.insertLast(curr_trace);
	}
	
	// else move up one level and create the next set of traces
	//
	else {
	  
	  // create a new trace, 
	  //
	  next_trace = new Trace(*curr_trace);
	  next_trace->setFrame((ulong)current_frame_d);

	  // decrement the history of the new trace
	  //
	  next_trace->setHistory(history_pool_d.popAndAllocate(next_trace->getHistory(), tmp_context));
	  
	  // the context must exist in the context pool in this case
	  //
	  next_trace->setSymbol(context_pool_d.get(*tmp_context));

	  // set the backpointer
	  //	  
	  next_trace->setBackPointer(curr_trace);
	  
	  // insert the trace into the trellis
	  //
	  if (search_mode_d == TRAIN) {
	    if (!insertNewPath(curr_trace, next_trace, 0)) {
	      return Error::handle(name(), L"propagateTracesUp", Error::ARG, __FILE__, __LINE__);
	    }
	  }
	  
	  // propagate all paths we can move to from this higher level trace
	  // (ISIP_BUG: this will only work for monophones and triphones)
	  //
	  tmp_vert = next_trace->getSymbol()->getLastVertex();	  

	  if (tmp_vert->isTerm()) {

	    // create a temporary trace
	    //
	    tmp_trace = new Trace(*next_trace);
	    tmp_trace->setFrame((ulong)current_frame_d);

	    // shift the history and set the backpointer
	    //
	    tmp_trace->setSymbol(context_pool_d.shiftAndAllocate(tmp_trace->getSymbol(), tmp_vert));

	    // set the backpointer
	    //
	    tmp_trace->setBackPointer(next_trace);

	    // add the trace to the temporary list
	    //
	    trace_list.insertLast(tmp_trace);

	    // print the debuging information
	    //
	    if (debug_level_d >= Integral::DETAILED) {
	      printNewPath(tmp_trace, next_trace);
	    }

	    // insert the trace into the trellis
	    //
	    if (search_mode_d == TRAIN) {
	      if (!insertNewPath(next_trace, tmp_trace, 0)) {
		return Error::handle(name(), L"propagateTracesUp", Error::ARG, __FILE__, __LINE__);
	      }
	    }	    
	  }

	  // vertex to propagate from is not terminal
	  //
	  else {

	    // are we applying nsymbol probabilities at this level?
	    //
	    if (getSearchLevel(level_num_a - 1).useSymbolGraph() ||
		getSearchLevel(level_num_a - 1).useNSymbol()) {

	      // error checking
	      //
	      if ((symbol = next_trace->getSymbol()) == (Context*)NULL) {
		return Error::handle(name(), L"propagateTracesUp", Error::ARG, __FILE__, __LINE__);
	      }
	      
	      SearchNode* search_node = symbol->getCentralVertex()->getItem();
	      
	      // 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();
	      
	      existing_trace = (Trace*)NULL;
	      if (search_node->mergeTrace(next_trace, current_frame_d,
					  existing_trace, index, order)) {
		
		if (debug_level_d >= Integral::DETAILED) {
		  printNewPath(next_trace, curr_trace);
		}

		if (getSearchLevel(level_num_a - 1).useSymbolGraph()) {
		  
		  // generate the symbol graph at this level
		  //
		  SymbolGraphNode* new_symbol_node = (SymbolGraphNode*)NULL;
		  SymbolGraphNode* old_symbol_node = (SymbolGraphNode*)NULL;
		  
		  GraphVertex<SearchNode>* nv = (GraphVertex<SearchNode>*)NULL;
		  nv = next_trace->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_trace->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 trace gets added to the search node
		    //      
		    if (existing_trace == (Trace*)NULL) {
		      
		      new_symbol_node =
			symbol_graph_d.insertNode((ulong)current_frame_d, symbol_str);
		      next_trace->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 trace: ");
			output.concat(next_trace);
			Console::put(output);
		      }
		    }
		    
		    // when the new trace replaces the existing in the search node
		    //
		    else {
		      
		      new_symbol_node = existing_trace->getSymbolNode();
		      next_trace->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 trace: ");
			output.concat(existing_trace);
			output.concat(L", to trace: ");
			output.concat(next_trace);			
			
			Console::put(output);
		      }		      
		    }
		    
		    // insert the old symbol graph node into the current nodes history
		    //
		    float ac_score = curr_trace->getScore();
		    float lm_score = curr_trace->getLMScore();
		    
		    long history_paths =
		      getSearchLevel(level_num_a - 1).getHistoryPaths();
		    
		    new_symbol_node->insertPrevNode(old_symbol_node, ac_score,
						    history_paths, lm_score);
		  }
		}
		
		// add the trace to the temporary list
		//
		trace_list.insertLast(next_trace);	      
	      }
	      
	      // conflict in add the trace to the search nodes list
	      //
	      else {
		
		// print debugging information
		//      
		if (debug_level_d >= Integral::DETAILED) { 
		  printDeletedPath(next_trace, curr_trace);
		}

		if (getSearchLevel(level_num_a - 1).useSymbolGraph()) {
		  
		  // generate the symbol graph at this level
		  //
		  SymbolGraphNode* new_symbol_node = (SymbolGraphNode*)NULL;
		  SymbolGraphNode* old_symbol_node = (SymbolGraphNode*)NULL;
		  
		  GraphVertex<SearchNode>* nv = (GraphVertex<SearchNode>*)NULL;
		  nv = next_trace->getSymbol()->getCentralVertex();
		  long symbol_id = nv->getItem()->getSymbolId();
		  
		  if (!getSearchLevel(level_num_a - 1).isDummySymbol(symbol_id)
		      && !nv->isStart() && !nv->isTerm()) {
		    
		    // when the new trace has a worse score that the existing trace
		    //      
		    new_symbol_node = existing_trace->getSymbolNode();
		    
		    // insert the old symbol graph node into the current nodes history
		    //
		    old_symbol_node = curr_trace->getSymbolNode();
		    
		    float ac_score = curr_trace->getScore();
		    float lm_score = curr_trace->getLMScore();
		    
		    long history_paths =
		      getSearchLevel(level_num_a - 1).getHistoryPaths();
		    
		    new_symbol_node->insertPrevNode(old_symbol_node, ac_score,
						    history_paths, lm_score);
		  }
		}
		
		// discard the new trace
		//
		delete next_trace;
		next_trace = (Trace*)NULL;
	      }
	    }

	    // propagate the traces forward when no nsymbol probabilities
	    // are being applied at this level
	    //
	    else {
	      
	      if (debug_level_d >= Integral::DETAILED) {
		printNewPath(next_trace, curr_trace);
	      }
	      
	      // add the trace to the temporary list
	      //
	      trace_list.insertLast(next_trace);		      
	    }
	  }
	}
	
	// bump the current trace off of the trace list
	//
	if (curr_trace->getRefCount() < 1) {
	  
	  // if this is at top level, we can not delete them since they
	  // are also referenced by the valid_hyps
	  //
	  if (level_num_a != (long)initial_level_d) {
	    Trace::deleteTrace(curr_trace, true);
	  }
	}
	trace_lists_d(level_num_a).gotoFirst();
      }
      
      // if this trace is not at the end of a subgraph then keep it on this
      // level
      //
      else {
	
	float curr_score = curr_trace->getScore();

	// update the maximal trace score for the beam pruning
	//
	if(getSearchLevel(level_num_a).useBeam()
	  && (curr_score > max_trace_scores_d(level_num_a)) ) {
	  max_trace_scores_d(level_num_a) = curr_score;
	}
      
	// keep the trace on this level
	//
	trace_lists_d(level_num_a).insertLast(curr_trace);
	trace_lists_d(level_num_a).gotoFirst();
      }
    }
  }

  // propagate the traces forward
  //
  while (!trace_list.isEmpty()) {    

    // retrieve the current trace and its central vertex
    // (ISIP_BUG: this will only work for monophones and triphones)    
    //
    trace_list.removeLast(curr_trace);
    curr_vert = curr_trace->getSymbol()->getLastVertex();

    // when the central vertex is a terminal
    //
    if (curr_vert->isTerm()) {

      // insert the traces in the higher level's trace list
      //
      trace_lists_d(level_num_a - 1).insertLast(curr_trace);
    }

    else {
      
      // generate traces to all next vertices
      //	  
      for (boolean more_paths = curr_vert->gotoFirst(); more_paths;
	   more_paths = curr_vert->gotoNext()) {
	
	// create a new trace, we store the extra trace so we
	// can later view the scores as they are added. this can
	// be changed later for efficiency
	//
	next_trace = new Trace(*curr_trace);
	next_trace->setFrame((ulong)current_frame_d);
	
	// has the context been previously generated?
	// if so reuse the context,
	// if not generate a new context and add it to the context pool
	//
	GraphArc<SearchNode>* tmp_arc = curr_vert->getCurr();
	
	next_trace->setSymbol(context_pool_d.shiftAndAllocate(next_trace->getSymbol(), tmp_arc->getVertex()));
	
	// add the trace to the search node's trace list
	//
	if (!addHypothesisPath(next_trace, curr_trace, tmp_arc,
			       level_num_a - 1)) {
	  return Error::handle(name(), L"propagateTracesUp", Error::ARG, __FILE__, __LINE__);
	}    
      }

      // delete curr_trace if no traces propagated from it have survived
      //
      if (curr_trace->getRefCount() < 1) {
	Trace::deleteTrace(curr_trace, true);
      }      
    }
  }
  
  // exit gracefully
  //
  return status;
}

// method: propagateInstancesDown
//
// arguments:
//  long level_num: (input) the level for which we will propagate instances
//
// return: logical error status
//
// propagate all instances down the hierarchy. any instances that are at the
// end of a subgraph are discarded
//
boolean HierarchicalSearch::propagateInstancesDown(long level_num_a) {

  // define local variables
  //
  Instance* curr_instance = (Instance*)NULL;
  Instance* tmp_instance = (Instance*)NULL;  

  GraphVertex<SearchNode>* curr_vert = (GraphVertex<SearchNode>*)NULL;
  GraphVertex<SearchNode>* start_vertex = (GraphVertex<SearchNode>*)NULL;
  
  SearchSymbol item;
  boolean status = 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).gotoFirst();
  
  if (level_num_a != getNumLevels() - 1) {
    
    // loop over all current instances and create the next level's instances
    //
    while (!instance_lists_d(level_num_a).isEmpty()) {
      
      // 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
	//
	Instance::deleteInstance(curr_instance, true);
      }

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

	// get the current history and its central vertex
	//
	Context curr_context(*curr_instance->getSymbol());	
	curr_vert = curr_context.getCentralVertex();

	// print debugging information
	//
	if (debug_level_d >= Integral::ALL) {
	  curr_context.print();
	}
	
	// if this vertex has a subgraph then descend and create the instances
	// at the next level
	//
	if (!curr_vert->isStart() && !curr_vert->isTerm()) {
	  
	  // we are propagating paths so set status to true
	  //
	  status = true;

	  // retrieve the current and lower levels
	  //
	  SearchLevel& sl_curr = getSearchLevel(level_num_a);
	  SearchLevel& sl_lower = getSearchLevel(level_num_a + 1);
	    	    
	  // determine the symbol id corresponding to the central context
	  //
	  long symbol_id = curr_context.getCentralVertex()->getItem()->getSymbolId();
	    
	  // when we are in context generation mode -
	  //   if we require context for this level then we save the context
	  //
	  Ulong* subgr_ind = (Ulong*)NULL;
	  Context* tmp_context = (Context*)NULL;	    
	  
	  if (context_generation_mode_d &&
	      ((long)context_level_d == level_num_a) &&
	      !getSearchLevel(level_num_a).isContextLessSymbol(symbol_id)) {
	    
	    String context;
	    curr_context.print(context);

	    // append the context to the sdb
	    //
	    Ulong tmp_value;
	    if (!context_hash_d.containsKey(context)) {
	      context_list_d.concat(context);
	      context_hash_d.insert(context, &tmp_value);
	    }
	  }
	  
	  // when we are in context generation mode -
	  //   determine the subgraph index for the current context
	  //	    	    
	  if (context_generation_mode_d &&
	      ((long)context_level_d == level_num_a)) {
	    
	    Context new_context(1);
	    GraphVertex<SearchNode>* central_vertex =
	      curr_context.getCentralVertex();
	    
	    // initialize central vertex of the context
	    //	      
	    new_context.assignAndAdvance((ulong)central_vertex);
	    
	    // convert context of vertices to search symbol indices
	    //	      
	    new_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;
	      new_context.print(context);
	      String output(L"No subgraph found for context ");
	      output.concat(context);
	      output.concat(L" at level ");
	      output.concat(level_num_a);
		Console::put(output);
		return Error::handle(name(), L"propagateInstancesDown", Error::ARG, __FILE__, __LINE__);	    
	    }
	    if (tmp_context != (Context*)NULL) {
	      delete tmp_context;
	    }	      	      
	  }
	  
	  // when we are NOT in context generation mode -
	  //   determine the subgraph index for the current context iff
	  //   the current symbol does not have context (context-less)
	  //	    
	  else if (getSearchLevel(level_num_a).isContextLessSymbol(symbol_id)
		   && !context_generation_mode_d) {
	    
	    // 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"propagateInstancesDown - left and right context lengths must be the same", Error::ARG, __FILE__, __LINE__);
	    }
	    
	    // initialize the left context
	    //
	    Context new_context(total_context_length, central_pos);
	    
	    GraphVertex<SearchNode>* central_vertex =
	      curr_context.getCentralVertex();
	    DiGraph<SearchNode>* parent_graph =
	      central_vertex->getParentGraph();
	    
	    // initialize left vertices of the context
	    //	      
	    for (long i = 0; i < left_context_length; i++) {
	      new_context.assignAndAdvance((ulong)parent_graph->getStart());
	    }
	    
	    // initialize central vertex of the context
	    //	      
	    new_context.assignAndAdvance((ulong)central_vertex);
	    
	    // initialize right vertices of the context
	    //	      
	    for (long i = 0; i < right_context_length; i++) {
	      new_context.assignAndAdvance((ulong)parent_graph->getTerm());
	    }

	    // convert context of vertices to search symbol indices
	    //	      
	    new_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;
	      new_context.print(context);
	      String output(L"No subgraph found for context ");
	      output.concat(context);
	      output.concat(L" at level ");
	      output.concat(level_num_a);
	      Console::put(output);
	      return Error::handle(name(), L"propagateInstancesDown", Error::ARG, __FILE__, __LINE__);	    
	    }
	    if (tmp_context != (Context*)NULL) {
	      delete tmp_context;
	    }	      
	  }
	  
	  // when we are NOT in context generation mode -
	  //   determine the subgraph index for the current context
	  //	    	    
	  else {

	    // convert context of vertices to search symbol indices
	    //	      	      
	    curr_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);
	      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
	  // 
	  start_vertex = (sl_lower.getSubGraph((long)*subgr_ind)).getStart();
	  
	  // when the next (lower) level has existing context we can simply
	  // extend that context - this is necessary for cross-word
	  //	  
	  if ((curr_instance->hasNextLevelContext()) &&
	      ((level_num_a + 1) != (getNumLevels() - 1))) {
	  
	    // generate a instance from the current vertex to the start vertex
	    // of its corresponding subgraph at the lower level
	    //
	    descend(curr_instance);
	    tmp_instance = new Instance(*curr_instance);
	    tmp_instance->setFrame((ulong)current_frame_d);
	    ascend(curr_instance);
	    
	    // set the backpointer
	    //
	    tmp_instance->setBackPointer(curr_instance);
	    
	    // set the start vertex context
	    //
	    Context* start_context = context_pool_d.initAndAllocate();
	    start_context = context_pool_d.shiftAndAllocate(start_context, start_vertex);

	    Context* prev_symbol = tmp_instance->getSymbol();
	    tmp_instance->setSymbol(start_context);
	    	    
	    // print the debuging information
	    //
	    if (debug_level_d >= Integral::DETAILED) {
	      printNewPath(tmp_instance, curr_instance);
	    }
	    
	    // insert the trace into the trellis
	    //
	    if (search_mode_d == TRAIN) {
	      if (!insertNewPath(curr_instance, tmp_instance, 0)) {
		return Error::handle(name(), L"propagateTracesUp", Error::ARG, __FILE__, __LINE__);
	      }
	    }
    
	    // extend the current context
	    //	    	  
	    if (!extendRightContexts(tmp_instance, level_num_a + 1,
				     prev_symbol)) {
	      return Error::handle(name(), L"propagateInstancesUp", Error::ARG, __FILE__, __LINE__);
	    }	    
	  }
	  
	  // when the next (lower) level does not have any previously generated
	  // context we need to start fresh and generate the initial context
	  //
	  else {

	    // determine the context length for the next level
	    //	    
	    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"propagateInstancesDown - left and right context lengths must be the same", Error::ARG, __FILE__, __LINE__);
	    }
	    
	    // has the context been previously generated?
	    // if so reuse the context,
	    // if not generate a new context and add it to the context pool
	    //
	    Context left_context(total_context_length, central_pos);	    
	    
	    for (long i = 0; i < central_pos; i++) {
	      left_context.assignAndAdvance((ulong)start_vertex);
	    }

	    // generate a instance from the current vertex to the start vertex
	    // of its corresponding subgraph at the lower level
	    //
	    tmp_instance = new Instance(*curr_instance);
	    tmp_instance->setFrame((ulong)current_frame_d);
	    
	    // set the backpointer
	    //
	    tmp_instance->setBackPointer(curr_instance);
	    
	    // set the start vertex context
	    //
	    Context* start_context = context_pool_d.initAndAllocate();
	    start_context = context_pool_d.shiftAndAllocate(start_context, start_vertex);
	    tmp_instance->setSymbol(start_context);
	    
	    // print the debuging information
	    //
	    if (debug_level_d >= Integral::DETAILED) {
	      printNewPath(tmp_instance, curr_instance);
	    }
	    
	    // insert the trace into the trellis
	    //
	    if (search_mode_d == TRAIN) {
	      if (!insertNewPath(curr_instance, tmp_instance, 0)) {
		return Error::handle(name(), L"propagateTracesUp", Error::ARG, __FILE__, __LINE__);
	      }
	    }
	    
	    // 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.pushAndAllocate(tmp_instance->getHistory(), curr_instance->getSymbol()));
	    
	    // loop over all paths in the subgraph and create new
	    // instances in the next level
	    //
	    Context* symbol = context_pool_d.get(left_context);
	    for (boolean more_paths = start_vertex->gotoFirst(); more_paths;
		 more_paths = start_vertex->gotoNext()) {	      
	      
	      // initialize central vertex of the context
	      //
	      GraphArc<SearchNode>* tmp_arc = start_vertex->getCurr();
	      GraphVertex<SearchNode>* target_vertex = tmp_arc->getVertex();

	      Context* left_center_context = context_pool_d.shiftAndAllocate(symbol, target_vertex);
	    
	      // output the debugging information
	      //
	      if (debug_level_d >= Integral::ALL) {
		left_center_context->print();
	      }
	    
	      // generate list of all possible right contexts
	      //
	      long right_context_length = sl_lower.getRightContext();	      
	      if (!initializeRightContexts(tmp_instance, *left_center_context,
			      level_num_a + 1, tmp_arc->getWeight(),
					   right_context_length)) {
		return Error::handle(name(), L"propagateInstancesDown", Error::ARG, __FILE__, __LINE__);
	      }
	    }       
	  }
	  
	  // bump the current instance off of the instance list
	  //
	  if (tmp_instance == (Instance*)NULL) {
	    if (curr_instance->getRefCount() < 1) {
	      Instance::deleteInstance(curr_instance, true);
	    }
	  } else {
	    if (tmp_instance->getRefCount() < 1) {
	      Instance::deleteInstance(tmp_instance, true);
	    }
	  }
	  instance_lists_d(level_num_a).gotoFirst();
	}
	
	// else if there is no subgraph for this node, we extend the search
	// along this level. this is a special case for the sentence start
	// search node. usually all nodes that we can propogate down will
	// either have a subgraph or it will be a terminal search node.
	//
	else {

	  boolean more_paths = curr_vert->gotoFirst();
	  if (more_paths) {
	    status = true;
	  }

	  // extend the current context
	  //	    	  
	  if (!extendRightContexts(curr_instance, level_num_a)) {
	    return Error::handle(name(), L"propagateInstancesUp", Error::ARG, __FILE__, __LINE__);
	  }

	  // bump the current instance off of the instance list
	  //
	  if (curr_instance->getRefCount() < 1) {
	    Instance::deleteInstance(curr_instance, true);
	  }
	  instance_lists_d(level_num_a).gotoFirst();
	}
      }
    }
  }
  
  // exit gracefully
  //
  return status;
}

// method: propagateTracesDown
//
// arguments:
//  long level_num: (input) the level for which we will propagate traces
//
// return: logical error status. if no propagations were made then return false
//
// propagate all traces down. any traces that are at the end of a subgraph are
// discarded.
//
boolean HierarchicalSearch::propagateTracesDown(long level_num_a) {

  // define local variables
  //
  SearchSymbol item;

  Trace* tmp_trace = (Trace*)NULL;  
  Trace* curr_trace = (Trace*)NULL;
  Trace* next_trace = (Trace*)NULL;
  
  GraphVertex<SearchNode>* curr_vert = (GraphVertex<SearchNode>*)NULL;
  GraphVertex<SearchNode>* tmp_vert = (GraphVertex<SearchNode>*)NULL;

  DoubleLinkedList<Float> score_list(DstrBase::USER);
  DoubleLinkedList<Context> right_context_list(DstrBase::USER);
  
  boolean status = false;
   
  // make sure there is at least one trace to propagate
  //
  if (trace_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 traces down the hierarchy -
  // there should be no traces left at this level when we are done
  //
  trace_lists_d(level_num_a).gotoFirst();
  
  if (level_num_a != getNumLevels() - 1) {
    
    // loop over all current traces and create the next level's traces
    //
    while (!trace_lists_d(level_num_a).isEmpty()) {
      
      // get the current vertex in the search graph
      //
      trace_lists_d(level_num_a).removeFirst(curr_trace);

      // delete this trace if it is now inactive
      //
      if (!curr_trace->isActive()) {

	// bump the current trace off of the trace list and delete it
	// and its backpath if necessary
	//
	Trace::deleteTrace(curr_trace, true);
      }

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

	// get the current history and its central vertex
	//
	Context* curr_context = curr_trace->getSymbol();
	curr_vert = curr_context->getCentralVertex();
	
	// if this vertex has a subgraph then descend and create the traces
	// at the next level
	//
	long symbol_id = curr_vert->getItem()->getSymbolId();
	
	if (!curr_vert->isStart() && !curr_vert->isTerm() &&
	    !getSearchLevel(level_num_a).isDummySymbol(symbol_id)) {
	  
	  // we are propagating paths so set status to true
	  //
	  status = true;

	  // 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
	  //
	  Ulong* subgr_ind = (Ulong*)NULL;
	  Context* tmp_context = (Context*)NULL;
	  Context* swap_context = curr_context;
	  Context copy_context(*curr_context);

	  // print the original context
	  //
	  if (debug_level_d >= Integral::DETAILED) {

	    // debug information
	    //
	    String context;
	    curr_context->print(context);
	    String output(L"curr_context org:");
	    output.concat(context);
	    Console::put(output);
	  }

	  // deal with the context less symbols
	  //
	  if (true ){
	    	  
	    // map all context less symbols to their central context only
	    //
	    long left_order = getSearchLevel(level_num_a).getLeftContext();
	    long right_order = getSearchLevel(level_num_a).getRightContext();
	  
	    // determine the symbol id corresponding to the central context
	    //
	    long curr_id =
	      copy_context.getCentralVertex()->getItem()->getSymbolId();

	    boolean context_less =
	      getSearchLevel(level_num_a).isContextLessSymbol(curr_id);

	    // set the left context
	    //
	    long index2 = 0;
	    GraphVertex<SearchNode>* curr_vertex =  NULL;

	    // replace the left context as necessary
	    //
	    for (int j=0; j < left_order; j++) {
	      curr_vertex = (GraphVertex<SearchNode>*)
		(ulong)copy_context(index2);
	      curr_id = curr_vertex->getItem()->getSymbolId();

	      // replace this symbol id as the no left context
	      //
	      if (context_less ||
		  getSearchLevel(level_num_a).isContextLessSymbol(curr_id)){
		copy_context(index2).assign(curr_vertex->
					      getParentGraph()->getStart());
	      }
	      index2++;
	    }

	    // never change the central symbol
	    //
	    index2++;
	  
	    // set the right context
	    //
	    for (int j=0; j < right_order; j++) {
	      curr_vertex = (GraphVertex<SearchNode>*)
		(ulong)copy_context(index2);
	      curr_id = curr_vertex->getItem()->getSymbolId();

	      // replace this symbol id as the no left context
	      //
	      if (context_less ||
		  getSearchLevel(level_num_a).isContextLessSymbol(curr_id)){
		copy_context(index2).assign(curr_vertex->
					      getParentGraph()->getTerm());
	      }
	      index2++;
	    }

	    // set curr context
	    //
	    curr_context = &copy_context;
	  }

	  if (debug_level_d >= Integral::DETAILED) {

	    // debug information
	    //
	    String context;
	    curr_context->print(context);
	    String output(L"curr_context new:");
	    output.concat(context);
	    Console::put(output);
	  }
	  
	  // add for word-internal context generation
	  // when we are in context generation mode -
	  //   if we require context for this level then we save the context
	  //
	  if (context_generation_mode_d &&
	      ((long)context_level_d == level_num_a)) {
	    
	    String context;
	    curr_context->print(context);
	    
	    // append the context to the sdb
	    //
	    Ulong tmp_value;
	    if (!context_hash_d.containsKey(context)) {
	      context_list_d.concat(context);
	      context_hash_d.insert(context, &tmp_value);
	    }
	  }

          // when we are in context generation mode -
	  //   determine the subgraph index for the current context
	  //
	  if (context_generation_mode_d &&
	      ((long)context_level_d == level_num_a)) {
	    
	    Context new_context(1);
	    GraphVertex<SearchNode>* central_vertex =
	      curr_context->getCentralVertex();
	    
	    // initialize central vertex of the context
	    //
	    new_context.assignAndAdvance((ulong)central_vertex);
	    
	    // convert context of vertices to search symbol indices
	    //
	    new_context.convert(tmp_context);
	    
	    // get the index corresponding to the current context
	    //
	    subgr_ind = sl_curr.getSubGraphIndex(*tmp_context);
	    
	  }
	  else {
	    curr_context->convert(tmp_context);
	    subgr_ind = sl_curr.getSubGraphIndex(*tmp_context);
	  }

	  // set curr context back
	  //
	  curr_context = swap_context;
	  swap_context = NULL;

	  // check with the sub graph id
	  //
	  if (subgr_ind == (Ulong*)NULL) {

	    // throw an error message and exit
	    //
	    String out(L"propagateTracesDown - no subgraph for context: ");
	    curr_context->debug(L"undefined context");
	    curr_trace->getSymbol()->print();	    
	    Console::put(out);
	    return Error::handle(name(), L"propagateTracesDown", Error::ARG,
				 __FILE__, __LINE__);	    
	  }
	  if (tmp_context != (Context*)NULL) {
	    delete tmp_context;
	  }
	    
	  if (debug_level_d >= Integral::DETAILED) {

	    // debug information
	    //
	    String context;
	    curr_context->print(context);
	    String output(L"curr_context ");
	    output.concat(context);
	    output.concat(L" at level ");
	    output.concat(level_num_a);
	    Console::put(output);
	    subgr_ind->debug(L"subgr_ind:");
	  }

	  // get the subgraph start vertex
	  // 
	  tmp_vert = (sl_lower.getSubGraph((long)*subgr_ind)).getStart();

	  // generate a trace from the current vertex to the start vertex
	  // of its corresponding subgraph at the lower level
	  //
	  tmp_trace = new Trace(*curr_trace);
	  tmp_trace->setFrame((ulong)current_frame_d);

	  // determine the context length for the next level
	  //
	  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"propagateTracesDown - left and right context lengths must be the same", Error::ARG, __FILE__, __LINE__);
	  }
	  
	  // 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_trace->setHistory(history_pool_d.pushAndAllocate(tmp_trace->getHistory(), curr_trace->getSymbol()));

	  // has the context been previously generated?
	  // if so reuse the context,
	  // if not generate a new context and add it to the context pool
	  //
	  Context left_context(total_context_length, central_pos);	    

	  for (long i = 0; i < central_pos; i++) {
	    left_context.assignAndAdvance((ulong)tmp_vert);
	  }

	  // set the start vertex context
	  //
	  Context* start_context = context_pool_d.initAndAllocate();
	  start_context = context_pool_d.shiftAndAllocate(start_context, tmp_vert);
	  tmp_trace->setSymbol(start_context);	  
	  
	  // set the backpointer
	  //
	  tmp_trace->setBackPointer(curr_trace);

	  // print the debuging information
	  //
	  if (debug_level_d >= Integral::DETAILED) {
	    printNewPath(tmp_trace, curr_trace);
	  }

	  // insert the trace into the trellis
	  //
	  if (search_mode_d == TRAIN) {
	    if (!insertNewPath(curr_trace, tmp_trace, 0)) {
	      return Error::handle(name(), L"propagateTracesUp", Error::ARG, __FILE__, __LINE__);
	    }
	  }
	    
	  // loop over all paths in the subgraph and create new traces in the
	  // next level
	  //
	  Context* symbol = context_pool_d.get(left_context);
	  for (boolean more_paths = tmp_vert->gotoFirst(); more_paths;
	       more_paths = tmp_vert->gotoNext()) {

	    // initialize central vertex of the context
	    //
	    GraphArc<SearchNode>* tmp_arc = tmp_vert->getCurr();
	    GraphVertex<SearchNode>* target_vertex = tmp_arc->getVertex();

	    Context* left_center_context = context_pool_d.shiftAndAllocate(symbol, target_vertex);	    

	    // output the debugging information
	    //
	    if (debug_level_d >= Integral::ALL) {
	      left_center_context->print();
	    }
	    
	    // generate list of all possible right contexts
	    //	    
	    long depth = getSearchLevel(level_num_a + 1).getRightContext();

	    score_list.setAllocationMode(DstrBase::USER);
	    right_context_list.setAllocationMode(DstrBase::USER);    

	    generateRightContexts(right_context_list, score_list,
				  left_center_context, depth, level_num_a + 1);

	    // output the debugging information
	    //
	    if (debug_level_d >= Integral::ALL) {
	      String output(L"Generated contexts for depth");
	      output.concat(depth);
	      Console::put(output);
	      long num = 0;
	      for (boolean more_items = right_context_list.gotoFirst();
		  more_items;
		  more_items = right_context_list.gotoNext()) {
		num++;
		String out(L" ");
		out.concat(num);
		out.concat(L":");
		String cont;
		right_context_list.getCurr()->print();
		out.concat(cont);
		Console::put(out);
	      }
	    }

	    // generate a new trace for each right context
	    //
	    for (boolean more_items =
		   (right_context_list.gotoFirst() && score_list.gotoFirst());
		 more_items;
		 more_items =
		   (right_context_list.gotoNext() && score_list.gotoNext())) {
	      
	      // create a new trace
	      //
	      next_trace = new Trace(*tmp_trace);
	      next_trace->setFrame((ulong)current_frame_d);

	      // factor in the symbol internal transition scores
	      //
	      float posterior_score = (float)(*score_list.getCurr());
	      
	      next_trace->setScore(next_trace->getScore() + posterior_score);

	      // 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_trace->setSymbol(right_context_list.getCurr());
	      
	      // add the trace to the search node's trace list.
	      //
	      if (!addHypothesisPath(next_trace, tmp_trace, tmp_arc,
				     level_num_a + 1)) {
		return Error::handle(name(), L"propagateTracesDown", Error::ARG, __FILE__, __LINE__);
	      }	      
	    }

	    // free allocated memory
	    //
	    right_context_list.clear();
  
	    score_list.setAllocationMode(DstrBase::SYSTEM);  
	    score_list.clear();	    
	  }
	    
	  // bump the current trace off of the trace list
	  //
	  if (tmp_trace->getRefCount() < 1) {
	    Trace::deleteTrace(tmp_trace, true);
	  }
	  trace_lists_d(level_num_a).gotoFirst();
	}
	
	// else if there is no subgraph for this node, we extend the search
	// along this level. for now, we don't allow a level to completely
	// skip sub-levels so if we get to the end of the graph at this level
	// we simply stop
	//
	else {
	  
	  boolean more_paths = curr_vert->gotoFirst();
	  if (more_paths) {
	    status = true;
	  }
	  
	  for ( ; more_paths; more_paths = curr_vert->gotoNext()) {
	    
	    // create a new trace
	    //
	    next_trace = new Trace(*curr_trace);
	    next_trace->setFrame((ulong)current_frame_d);
	    
	    // update the history with the next node at this level
	    //
	    GraphArc<SearchNode>* tmp_arc = curr_vert->getCurr();
	    
	    next_trace->setSymbol(context_pool_d.shiftAndAllocate(curr_trace->getSymbol(), tmp_arc->getVertex()));
	    
	    // add the trace to the search node's trace list to make sure we
	    // propagate it before exiting
	    //
	    if (!addHypothesisPath(next_trace, curr_trace, tmp_arc,
				   level_num_a)) {
	      return Error::handle(name(), L"propagateTracesDown", Error::ARG, __FILE__, __LINE__);
	    }	    
	  }
	  
	  // bump the current trace off of the trace list
	  //
	  if (curr_trace->getRefCount() < 1) {
	    Trace::deleteTrace(curr_trace, true);
	  }
	  trace_lists_d(level_num_a).gotoFirst();
	}
      }
    }
  }
  
  // exit gracefully
  //
  return status;
}

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

  // define local variables
  //
  Trace* curr_trace = (Trace*)NULL;
  float threshold = max_trace_scores_d(level_num_a)
    - (*h_digraph_d)(level_num_a).getBeamThreshold();
  float curr_score;

  long pruned_traces = 0;
  long orig_length = trace_lists_d(level_num_a).length();
  
  // loop over all active traces at this level
  //
  boolean more_traces = trace_lists_d(level_num_a).gotoFirst();
  
  while (more_traces) {

    curr_trace = trace_lists_d(level_num_a).getCurr();
    curr_score = curr_trace->getScore();

    if (curr_score > max_trace_scores_d(level_num_a)) {
      return Error::handle(name(), L"beamPruneTrace - bad maximal score", Error::ARG, __FILE__, __LINE__);      
    }
      
    // prune the traces 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", trace:" );
	output.concat(curr_trace);
	Console::put(output);
      }

      if (trace_lists_d(level_num_a).isLast()) {
	more_traces = false;
      }
	  
      // bump the current trace off of the trace list
      //
      trace_lists_d(level_num_a).remove();
      
      if (curr_trace->getRefCount() < 1) {
	Trace::deleteTrace(curr_trace, true);
      }
      pruned_traces++;
    }
    
    else {
      
      if (debug_level_d >= Integral::ALL) {
	String output(L"score is not smaller than threshold: ");
	output.concat(curr_score);
	output.concat(L", trace:" );
	output.concat(curr_trace);
	Console::put(output);
      }
      
      more_traces = trace_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 traces: ");
    output.concat(orig_length);
    output.concat(L", pruned traces: ");
    output.concat(pruned_traces);
    output.concat(L", current traces: ");
    output.concat(trace_lists_d(level_num_a).length());
    output.concat(L", threshold: ");
    output.concat(threshold);
    output.concat(L", max trace score: ");
    output.concat(max_trace_scores_d(level_num_a));
    output.concat(L"\n");
    Console::put(output);
  }
  
  // exit gracefully
  //
  return true;
}

// method: beamPruneInstance
//
// arguments:
//  long level_num: (input) the level for which we will propagate traces
//
// return: logical error status.
//
boolean HierarchicalSearch::beamPruneInstance(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"beamPruneInstance - 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: instancePruneInstance
//
// arguments:
//  long level_num: (input) the level for which we will propagate traces
//
// return: logical error status.
//
boolean HierarchicalSearch::instancePruneInstance(long level_num_a) {

  // define local variables
  //
  Instance* curr_instance = (Instance*)NULL;
  float curr_score = 0.0;
  
  long pruned_instances = 0;  
  float instance_threshold = -0.5e10;  
  
  long num_instances = instance_lists_d(level_num_a).length();  
  long instance_limit = (*h_digraph_d)(level_num_a).getInstanceThreshold();

  if ((num_instances > instance_limit) && (instance_limit > 0)) {

    // sort all the instances by score in descending order
    //    
    instance_lists_d(level_num_a).sort(Integral::DESCENDING,
				       DstrBase::RAND_QUICK);

    // get the score at the cut-off instance
    //
    instance_lists_d(level_num_a).gotoPosition(instance_limit);
    curr_instance = instance_lists_d(level_num_a).getCurr();    
    instance_threshold = curr_instance->getScore();
  }

  // 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();
    
    // prune away the instance if it is below the threshold
    //
    if ((instance_threshold > 0.0) &&
	(instance_threshold > curr_score)) {
      
      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(num_instances);
    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(instance_threshold);
    output.concat(L"\n");
    Console::put(output);
  }
  
  // exit gracefully
  //
  return true;    
}

// method: instancePruneTrace
//
// arguments:
//  long level_num: (input) the level for which we will propagate traces
//
// return: logical error status.
//
boolean HierarchicalSearch::instancePruneTrace(long level_num_a) {
            
  // define local variables
  //
  Trace* curr_trace = (Trace*)NULL;
  float curr_score = 0.0;
  
  long pruned_traces = 0;  
  float trace_threshold = -0.5e10;  
  
  long num_traces = trace_lists_d(level_num_a).length();  
  long trace_limit = (*h_digraph_d)(level_num_a).getInstanceThreshold();

  if ((num_traces > trace_limit) && (trace_limit > 0)) {

    // sort all the traces by score in descending order
    //    
    trace_lists_d(level_num_a).sort(Integral::DESCENDING,
				       DstrBase::RAND_QUICK);

    // get the score at the cut-off trace
    //
    trace_lists_d(level_num_a).gotoPosition(trace_limit);
    curr_trace = trace_lists_d(level_num_a).getCurr();    
    trace_threshold = curr_trace->getScore();
  }

  // loop over all active traces at this level
  //
  boolean more_traces = trace_lists_d(level_num_a).gotoFirst();
  
  while (more_traces) {
    
    curr_trace = trace_lists_d(level_num_a).getCurr();
    curr_score = curr_trace->getScore();
    
    // prune away the trace if it is below the threshold
    //
    if ((trace_threshold > 0.0) &&
	(trace_threshold > curr_score)) {
      
      if (debug_level_d >= Integral::ALL) {
	String output(L"score is smaller than threshold: ");
	output.concat(curr_score);
	output.concat(L", trace:" );
	output.concat(curr_trace);
	Console::put(output);
      }
      
      if (trace_lists_d(level_num_a).isLast()) {
	more_traces = false;
      }
      
      // bump the current trace off of the trace list
      //
      trace_lists_d(level_num_a).remove();
      
      if (curr_trace->getRefCount() < 1) {
	Trace::deleteTrace(curr_trace, true);
      }      
      pruned_traces++;	
    }
    
    else {
      
      if (debug_level_d >= Integral::ALL) {
	String output(L"score is not smaller than threshold: ");
	output.concat(curr_score);
	output.concat(L", trace:" );
	output.concat(curr_trace);
	Console::put(output);
      }
      
      more_traces = trace_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 traces: ");
    output.concat(num_traces);
    output.concat(L", pruned traces: ");
    output.concat(pruned_traces);
    output.concat(L", current traces: ");
    output.concat(trace_lists_d(level_num_a).length());
    output.concat(L", threshold: ");
    output.concat(trace_threshold);
    output.concat(L"\n");
    Console::put(output);
  }
        
  // exit gracefully
  //
  return true;    
}
