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

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

#ifndef ISIP_SOF
#include <Sof.h>
#endif

// method: generateContextMapping
//
// arguments:
//  long left_order: (input) left context order
//  long right_order: (input) right context order
//  Filename& context_list: (input) context list file name
//  Vector<SearchLevel>& search_levels: (input) vector of search levels
//  boolean wint_flag: (input) wint flag
//
// return: a boolean indicating status
//
// this method generates the comtext map for the current search level
//
boolean SearchLevel::generateContextMapping(long left_order_a,
					    long right_order_a,
					    long num_levels_a,
					    Filename& context_list_a,
					    SearchLevel& level_a,
					    boolean wint_flag_a) {

  // declare local variables
  //
  Filename list_item;
  ContextMap context_map;  
  Vector<SearchSymbol> context;
  
  Sof input_sof;
  SearchSymbol item;  
  Sdb symbol_list_sdb;
  SearchSymbol substring;
  SearchSymbol central_symbol;
  
  // context map cannot be generate for the terminal (acoustic) level
  //
  if ((long)level_index_d == num_levels_a - 1) {
    return Error::handle(name(), L"generateContextMapping", Error::ARG, __FILE__, __LINE__);
  }
  
  // open the context list
  //
  if (!input_sof.open(context_list_a)) {
    return Error::handle(name(), L"generateContextMapping", Error::ARG, __FILE__, __LINE__);
  }

  // read the sdb list
  //
  symbol_list_sdb.read(input_sof, (long)level_index_d);
  input_sof.close();

  // if the list is empty do nothing
  //
  if (symbol_list_sdb.length() == 0) {
    context_list_a.debug(L"context list");    
    level_index_d.debug(L"search level");
    Error::handle(name(), L"generateContextMapping - cannot find the context list corresponding to the specified search level", Error::ARG, __FILE__, __LINE__);
    return true;
  }
  
  // loop over each element in the sdb list
  //
  long index1 = 0;
  Boolean tmp_value;
  HashTable<SearchSymbol, Boolean> tmp_hash;

  // set the capacity of the context map
  //
  long num_symbols = symbol_table_d.length();
  long capacity = (long)Integral::pow((double)num_symbols, (double)(left_order_a + right_order_a + 1));  
  context_map_d.setCapacity(capacity);

  for (symbol_list_sdb.gotoFirst();
       symbol_list_sdb.getName(list_item);
       symbol_list_sdb.gotoNext()) {

    // get the current context form the list
    //
    list_item.getName(item);
    item.trim();

    // get the number of left and right symbols in the context
    //
    long index2 = 0;
    long left = item.countTokens(PARAM_DELIM1) - 1; 
    long right = item.countTokens(PARAM_DELIM2) - 1;   

    // set the context length
    //
    context.setLength(left_order_a + right_order_a + 1);
    
    // when the left context is less that the order prepend a no left context
    //
    if (left < left_order_a) {
      for (int i = left; i < left_order_a; i++) {

	if (index2 >= context.length()) {

	  item.debug(L"context");
	  Long lorder(left_order_a);
	  lorder.debug(L"left context length");

	  return Error::handle(name(), L"generateContextMapping - symbol context length is greater that the one specified at the search level", Error::ARG, __FILE__, __LINE__);
	}
	
	context(index2++).assign(SearchSymbol::NO_LEFT_CONTEXT);
      }
    }
    
    // retrieve each symbol of the context and set up the map
    //
    long pos = 0;
    long curr_symbol = 0;
    boolean contextless_symbol = false;

    while (item.tokenize(substring, pos, PARAM_DELIM3)) {

      // check if the symbol is present in the symbol table
      //
      substring.trim();
      if (!isValidSymbol(getSymbolIndex(substring))) {
	substring.debug(L"symbol");
	return Error::handle(name(), L"generateContextMapping", Error::ARG, __FILE__, __LINE__);
      }

      // check if the symbol is the central context
      //
      if (curr_symbol == left_order_a) {

	// is this symbol without context
	//
	if (isContextLessSymbol(getSymbolIndex(substring))) {
	  contextless_symbol = true;
	}
      }

      // assign the symbol to the context
      //
      curr_symbol++;

      if (index2 >= context.length()) {

	item.debug(L"context");
	Long lorder(left_order_a);
	lorder.debug(L"left context length");
	Long rorder(right_order_a);
	rorder.debug(L"right context length");
	
	return Error::handle(name(), L"generateContextMapping - symbol context length is greater that the one specified at the search level", Error::ARG, __FILE__, __LINE__);
      }
      
      context(index2++).assign(substring);
    }

    // when the right context is less that the order append a no right context
    //
    if (right < right_order_a) {    
      for (int i = right; i < right_order_a; i++) {

	if (index2 >= context.length()) {

	  item.debug(L"context");
	  Long rorder(right_order_a);
	  rorder.debug(L"right context length");
	
	  return Error::handle(name(), L"generateContextMapping - symbol context length is greater that the one specified at the search level", Error::ARG, __FILE__, __LINE__);
	}
	
	context(index2++).assign(SearchSymbol::NO_RIGHT_CONTEXT);	
      }
    }
    
    // assign the current context to the context map (optional)
    //
    if (!contextless_symbol || wint_flag_a) {

      // retrieve the central context symbol
      //      
      central_symbol.assign(context(left_order_a));
      
      context_map.setContext(context);
      
      if (tmp_hash.containsKey(central_symbol)) {
	createCopy(num_levels_a, central_symbol, context_map, level_a);
      }
      else {
	tmp_hash.insert(central_symbol, &tmp_value);
	context_map.setContextIndex((ulong)getSymbolIndex(central_symbol));
      }
      
      context_map_d.setLength(index1 + 1);
      context_map_d(index1++).assign(context_map);
    }
  }

  // map all context less symbols to their central context only
  //
  context.setLength(left_order_a + right_order_a + 1);

  for (long i = 0; i < contextless_symbol_table_d.length(); i++) {

    // set the left context
    //
    long index2 = 0;
    for (int j = 0; j < left_order_a; j++) {
      context(index2++).assign(SearchSymbol::NO_LEFT_CONTEXT);
    }

    // set the central context
    //
    central_symbol.assign(contextless_symbol_table_d(i));
    context(index2++).assign(central_symbol);

    // set the right context
    //
    for (int j = 0; j < right_order_a; j++) {
      context(index2++).assign(SearchSymbol::NO_RIGHT_CONTEXT);
    }
    
    if (!tmp_hash.containsKey(central_symbol)) {
      
	tmp_hash.insert(central_symbol, &tmp_value);
	context_map.setContext(context);
	context_map.setContextIndex((ulong)getSymbolIndex(central_symbol));
	context_map_d.setLength(index1 + 1);    
	context_map_d(index1++).assign(context_map);
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: createCopy
//
// arguments:
//  SearchSymbol& central_symbol: (input) symbol of central context
//  ContextMap& context_map: (input) contex map of the central symbol
//  Vector<SearchLevel>& level: (input) all the search levels
//
// return: a boolean indicating status
//
// this method creates a copy of underlying models for every context that
// shares the same central search symbol
//
boolean SearchLevel::createCopy(long num_levels_a,
				SearchSymbol& central_symbol_a,
				ContextMap& context_map_a,
				SearchLevel& level_a) {

  // declare local variables
  //  
  long tmp_index = 0;
  long curr_index = 0;
  long symbol_index = 0;
  long central_index = 0;
  long stat_model_index = 0;
  
  Long val;
  StatisticalModel model_copy;
  DiGraph<SearchNode> graph_copy;

  // retrieve the symbol index of the central context symbol
  //
  central_index = getSymbolIndex(central_symbol_a);
  
  // retrieve the symbol table of the next level in the hierarchy
  //
  Vector<SearchSymbol>& symbol_table =
    level_a.getSymbolTable();

  // retrieve the subgraphs of the next level in the hierarchy
  //
  Vector<DiGraph < SearchNode> >& sub_graphs =
    level_a.getSubGraphs();  

  // retrieve the stat models of the next level in the hierarchy
  //
  Vector<StatisticalModel>& stat_models =
    level_a.getStatisticalModels();

  // retrieve the symbol hash table of the next level in the hierarchy
  //
  HashTable<SearchSymbol, Long>& symbol_hash =
    level_a.getSymbolHashTable();  

  // create a copy of the subgraph corresponding to the central contex symbol
  // and append the copy to the vector of subgraphs
  //
  sub_graphs(central_index).setAllocationMode(DstrBase::SYSTEM);
  graph_copy.assign(sub_graphs(central_index));
  
  graph_copy.setAllocationMode(DstrBase::USER);
  sub_graphs(central_index).setAllocationMode(DstrBase::USER);
  sub_graphs.concat(graph_copy);

  // set the context map index to the position of the appended copy
  //
  curr_index = sub_graphs.length() - 1;
  context_map_a.setContextIndex((ulong)curr_index);
  
  // make copies of the states and models (only at the acoustic level)
  //	
  if ((long)level_index_d == num_levels_a - 2) {
    
    // loop over each vertex in the subgraph of the appended copy
    //
    SearchSymbol ss;
    for (boolean more = sub_graphs(curr_index).gotoFirst();
	 more;
	 more = sub_graphs(curr_index).gotoNext()) {
      
      // retrieve the symbol index corresponding to the current vertex
      //
      symbol_index = sub_graphs(curr_index).getCurr()->getItem()->getSymbolId();
      
      // create a new search symbol and append it to the symbol table
      //
      ss.assign(L"S_");
      tmp_index = (long)symbol_table.length() + 1;
      ss.concat(tmp_index);
      while (symbol_table.contains(&ss)) {
	ss.assign(L"S_");
	ss.concat(++tmp_index);
      }
      symbol_table.concat(ss);

      // set the symbol id for the search node
      //
      val.assign(symbol_table.length() - 1);      
      sub_graphs(curr_index).getCurr()->getItem()->setSymbolId((long)val);
      
      // make a copy of the statistical model if applicable
      //
      if (stat_models.length() > 0) {

	// make a copy of the statistical model
	//
	stat_model_index = (long)(*symbol_hash.get(symbol_table(symbol_index)));
	model_copy.assign(stat_models(stat_model_index));
	stat_models.concat(model_copy);
	
	// create a new entry in the symbol hash table
	//
	val.assign(stat_models.length() - 1);
	symbol_hash.insert(ss, &val);
      }

      else {
	
	// create a new entry in the symbol hash table
	//
	symbol_hash.insert(ss, &val);
      }
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: loadTransitionAccumulators
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method reads the transition accumulators from file
//
boolean SearchLevel::loadTransitionAccumulators(Sof& sof_a, long tag_a) {

  // if no tag is specified, the tag should be this level number
  //    
  if (tag_a == DEF_TAG) {
    tag_a = (long)level_index_d;
  }

  // make sure we skip the top level
  //
  if (tag_a == 0) {
    return true;
  }
  
  // make sure there are valid sub graphs at this level
  //
  if (sub_graphs_d.length() == 0) {
    return true;
  }

  // declare a temporary holder for the Graph<Ulong>  and read them from file
  // 
  Vector< DiGraph<Ulong> > tmp_graphs;
  if (!tmp_graphs.read(sof_a, tag_a, PARAM_MODEL)) {
    return false;
  }

  long num_graphs = tmp_graphs.length();

  if (num_graphs != sub_graphs_d.length()) {
    return Error::handle(name(), L"loadTransitionAccumulators - number of sub graphs loaded from the accumulator file does not match the number of sub graphs at this level", Error::ARG, __FILE__, __LINE__);
  }
  
  // loop over each sub graph in tmp_graphs
  //
  for (long k = 0; k < num_graphs; k++) {

    long num_vertices = tmp_graphs(k).length();    
    
    if (num_vertices != sub_graphs_d(k).length()) {
      return Error::handle(name(), L"loadTransitionAccumulators - number of vertices loaded from the accumulator file does not match the number of vertices at this level", Error::ARG, __FILE__, __LINE__);
    }
    
    // loop over each vertex adjacent to the start vertex
    //
    GraphVertex<Ulong>* tmp_graph_vertex = tmp_graphs(k).getStart();
    GraphVertex<SearchNode>* sub_graph_vertex = sub_graphs_d(k).getStart();

    long num_arcs = tmp_graph_vertex->length();

    if (num_arcs != sub_graph_vertex->length()) {
      return Error::handle(name(), L"loadTransitionAccumulators - number of arcs loaded from the accumulator file does not match the number of arcs at this level", Error::ARG, __FILE__, __LINE__);
    }
    
    // loop over each arc corresponding to the current vertex
    //
    boolean arc_more = false;
    for (arc_more = (tmp_graph_vertex->gotoFirst() &
		     sub_graph_vertex->gotoFirst());
	 arc_more;
	 arc_more = (tmp_graph_vertex->gotoNext() &
		     sub_graph_vertex->gotoNext())) {      
      
      // read the accumulator value
      //
      sub_graph_vertex->getCurr()->setAccumulator(sub_graph_vertex->getCurr()->getAccumulator() + tmp_graph_vertex->getCurr()->getWeight());
    }
    
    // loop over each vertex in the current subgraph
    //
    boolean vertex_more = false;
    for (vertex_more = (sub_graphs_d(k).gotoFirst() &
			tmp_graphs(k).gotoFirst());
	 vertex_more;
	 vertex_more = (sub_graphs_d(k).gotoNext() &
			tmp_graphs(k).gotoNext())) {
      
      // retrieve the current vertices
      //
      GraphVertex<Ulong>* tmp_graph_vertex = tmp_graphs(k).getCurr();
      GraphVertex<SearchNode>* sub_graph_vertex = sub_graphs_d(k).getCurr();

      num_arcs = tmp_graph_vertex->length();

      if (num_arcs != sub_graph_vertex->length()) {
	return Error::handle(name(), L"loadTransitionAccumulators - number of arcs loaded from the accumulator file does not match the number of arcs at this level", Error::ARG, __FILE__, __LINE__);
      }
      
      // loop over each arc corresponding to the current vertex
      //
      arc_more = false;
      for (arc_more = (tmp_graph_vertex->gotoFirst() &
		       sub_graph_vertex->gotoFirst());
	   arc_more;
	   arc_more = (tmp_graph_vertex->gotoNext() &
		       sub_graph_vertex->gotoNext())) {      
	
	// read the accumulator value
	//
	sub_graph_vertex->getCurr()->setAccumulator(sub_graph_vertex->getCurr()->getAccumulator() + tmp_graph_vertex->getCurr()->getWeight());
      }
    }
  }
  
  // exit gracefully
  //
  return true;  
}

// method: loadEmissionAccumulators
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method reads the emission accumulators from file
//
boolean SearchLevel::loadEmissionAccumulators(Sof& sof_a, long tag_a) {

  // if no tag is specified, the tag should be this level number
  //    
  if (tag_a == DEF_TAG) {
    tag_a = (long)level_index_d;
  }

  // make sure we skip the top level
  //
  if (tag_a == 0) {
    return true;
  }
  
  // make sure there are valid statistical models at this level
  //
  if (stat_models_d.length() == 0) {
    return true;
  }

  // loop over each statistical model and retrieve their accumulators
  //
  for (long i = 0; i < stat_models_d.length(); i++) {

    // read the accumulators corresponding to the current model
    //
    stat_models_d(i).readAccumulator(sof_a, (long)i);
  }
  
  // exit gracefully
  //
  return true;  
}

// method: storeTransitionAccumulators
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method writes the transition accumulators to file
//
boolean SearchLevel::storeTransitionAccumulators(Sof& sof_a, long tag_a) {

  // make sure the symbol table has been loaded
  //
  long index = 0;
  long dst_index = 0;
  boolean is_eps = false;  
  long num_graphs = sub_graphs_d.length();
  long num_symbols = symbol_table_d.length();
  
  if (num_symbols == 0) {
    return true;
  }
  
  // write the symbol table
  //
  storeSymbols(sof_a, tag_a);

  // if no tag is specified, the tag should be its level number
  //
  if (tag_a == DEF_TAG) {
    tag_a = (long)level_index_d;
  }

  // make sure we skip the top level
  //
  if (tag_a == 0) {
    return true;
  }
  
  // declare a Vector of Graph<Ulong> to be written to file
  //
  Ulong tmp_item;
  SearchNode* snode;  
  GraphArc<SearchNode>* dst_arc;
  GraphVertex<SearchNode>* dst_vertex;  
  Vector< DiGraph<Ulong> > tmp_graphs;
  
  // convert each Graph<SearchNode> > to Graph<Ulong>
  //
  tmp_graphs.setLength(sub_graphs_d.length());
  for (long k = 0; k < num_graphs; k++) {

    // set the properties of graphs
    //
    long num_vertices = sub_graphs_d(k).length() + 2;
    tmp_graphs(k).setWeighted(sub_graphs_d(k).isWeighted());

    // insert all vertices corresponding the current graph index
    //
    index = 0;
    GraphVertex<SearchNode>* vertices[num_vertices];
    GraphVertex<Ulong>* tmp_vertices[num_vertices];

    // add the start vertices to the arrays
    //
    vertices[index] = sub_graphs_d(k).getStart();
    tmp_vertices[index++] = tmp_graphs(k).getStart();

    // add the rest of the vertices to the arrays
    //
    for (boolean more = sub_graphs_d(k).gotoFirst(); more;
	 more = sub_graphs_d(k).gotoNext()) {

      // get the current vertex
      //
      vertices[index] = const_cast<GraphVertex<SearchNode>* >(sub_graphs_d(k).getCurr());

      // get the search node corresponding to the vertex
      //
      snode = vertices[index]->getItem();

      // insert a vertex in the Graph<Ulong> for the current vertex
      // using the symbol ID of the vertex as the node item
      //
      tmp_item.assign(snode->getSymbolId());
      tmp_vertices[index++] = tmp_graphs(k).insertVertex(&tmp_item);
    }

    // add the term vertices to the arrays
    //
    vertices[num_vertices - 1] = sub_graphs_d(k).getTerm();
    tmp_vertices[num_vertices - 1] = tmp_graphs(k).getTerm();    
    
    // connect all vertices corresponding to the current graph index
    //
    for (long i = 0; i < num_vertices; i++) {
      boolean arcs_remain = vertices[i]->gotoFirst();
      while (arcs_remain) {

	// get the destination vertex
	//
	dst_arc = vertices[i]->getCurr();
	dst_vertex = dst_arc->getVertex();
	is_eps = dst_arc->getEpsilon();
	
	// find the destination vertex
	//
	dst_index = -1;
	for (long j = 0; j < num_vertices; j++) {
	  if (dst_vertex == vertices[j]) {
	    dst_index = j;
	    break;
	  } 	  
	}

	// error - when we cannot find a destination index
	//
	if (dst_index == -1) {
	  return Error::handle(name(), L"storeTransitionAccumulators", Error::ARG, __FILE__, __LINE__);
	}

	// connect the vertices in the Graph<Ulong>
	//
	tmp_graphs(k).insertArc(tmp_vertices[i], tmp_vertices[dst_index],
				is_eps, (double)dst_arc->getAccumulator());
	arcs_remain = vertices[i]->gotoNext();
      }
    }
  }

  // write the Vector of Graph<Ulong> to file
  //
  if (!tmp_graphs.write(sof_a, tag_a, PARAM_MODEL)) {
    return false;
  }
  
  // exit gracefully
  //
  return true;    
}

// method: storeEmissionAccumulators
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method writes the emission accumulators to file
//
boolean SearchLevel::storeEmissionAccumulators(Sof& sof_a, long tag_a) {

  // if no tag is specified, the tag should be this level number
  //    
  if (tag_a == DEF_TAG) {
    tag_a = (long)level_index_d;
  }

  // make sure we skip the top level
  //
  if (tag_a == 0) {
    return true;
  }
  
  // make sure there are valid statistical models at this level
  //
  if (stat_models_d.length() == 0) {
    return true;
  }

  // loop over each statistical model and store their accumulators
  //
  for (long i = 0; i < stat_models_d.length(); i++) {

    // write the accumulators corresponding to the current model
    //
    stat_models_d(i).writeAccumulator(sof_a, (long)i);
  }

  // exit gracefully
  //
  return true;
}

// method: loadContextLessSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method read the context less symbol table from Sof file
//
boolean SearchLevel::loadContextLessSymbols(Sof& sof_a, long tag_a) {

  // if no tag is specified, the tag should be this level number
  //    
  if (tag_a == DEF_TAG) {
    tag_a = (long)level_index_d;
  }
  
  // read the context less symbol table if being present in the file
  //  
  if (contextless_symbol_table_d.read(sof_a, tag_a,
				      PARAM_CONTEXTLESS_SYMBOL)) {
    
    // verify that the context less symbols are a subset of all valid symbols
    //
    SearchSymbol symbol;
    for (int i = 0; i < contextless_symbol_table_d.length(); i++) {
      symbol.assign(contextless_symbol_table_d(i));
      if (!isValidSymbol(getSymbolIndex(symbol))) {
	symbol.debug(L"invalid context less symbol");
	return Error::handle(name(), L"loadContextLessSymbols: there is a context less symbol that does not appear in the symbol table", SearchLevel::ERR, __FILE__, __LINE__);	
      }
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: storeContextLessSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method writes the ContextLess symbol table from Sof file
//
boolean SearchLevel::storeContextLessSymbols(Sof& sof_a, long tag_a) {

  // if no tag is specified, the tag should be this level number
  //  
  if (tag_a == DEF_TAG) {
    tag_a = (long)level_index_d;
  }
  
  // store the symbol table and exit
  //
  return contextless_symbol_table_d.write(sof_a, tag_a,
					  PARAM_CONTEXTLESS_SYMBOL);
}

// method: loadSkipSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method read the context less symbol table from Sof file
//
boolean SearchLevel::loadSkipSymbols(Sof& sof_a, long tag_a) {

  // if no tag is specified, the tag should be this level number
  //    
  if (tag_a == DEF_TAG) {
    tag_a = (long)level_index_d;
  }
  
  // read the context less symbol table if being present in the file
  //  
  if (skip_symbol_table_d.read(sof_a, tag_a,
				      PARAM_SKIP_SYMBOL)) {
    
    // verify that the context less symbols are a subset of all valid symbols
    //
    SearchSymbol symbol;
    for (int i = 0; i < skip_symbol_table_d.length(); i++) {
      symbol.assign(skip_symbol_table_d(i));
      if (!isValidSymbol(getSymbolIndex(symbol))) {
	symbol.debug(L"invalid context less symbol");
	return Error::handle(name(), L"loadSkipSymbols: there is a context less symbol that does not appear in the symbol table", SearchLevel::ERR, __FILE__, __LINE__);	
      }
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: storeSkipSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method writes the Skip symbol table from Sof file
//
boolean SearchLevel::storeSkipSymbols(Sof& sof_a, long tag_a) {

  // if no tag is specified, the tag should be this level number
  //  
  if (tag_a == DEF_TAG) {
    tag_a = (long)level_index_d;
  }
  
  // store the symbol table and exit
  //
  return skip_symbol_table_d.write(sof_a, tag_a,
					  PARAM_SKIP_SYMBOL);
}

// method: loadDummySymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method read the dummy symbol table from Sof file
//
boolean SearchLevel::loadDummySymbols(Sof& sof_a, long tag_a) {

  // if no tag is specified, the tag should be this level number
  //    
  if (tag_a == DEF_TAG) {
    tag_a = (long)level_index_d;
  }
  
  // read the dummy symbol table if being present in the file
  //  
  if (dummy_symbol_table_d.read(sof_a, tag_a, PARAM_DUMMY_SYMBOL)) {
    
    // verify that the dummy symbols are a subset of all valid symbols
    //
    SearchSymbol symbol;
    for (int i = 0; i < dummy_symbol_table_d.length(); i++) {
      symbol.assign(dummy_symbol_table_d(i));
      if (!isValidSymbol(getSymbolIndex(symbol))) {
	symbol.debug(L"invalid dummy symbol");
	return Error::handle(name(), L"loadDummySymbols: there is a dummy symbol that does not appear in the symbol table", SearchLevel::ERR, __FILE__, __LINE__);	
      }
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: storeDummySymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method writes the dummy symbol table from Sof file
//
boolean SearchLevel::storeDummySymbols(Sof& sof_a, long tag_a) {

  // if no tag is specified, the tag should be this level number
  //  
  if (tag_a == DEF_TAG) {
    tag_a = (long)level_index_d;
  }
  
  // store the symbol table and exit
  //
  return dummy_symbol_table_d.write(sof_a, tag_a, PARAM_DUMMY_SYMBOL);
}

// method: loadNonAdaptSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method read the non adaptation symbol table from Sof file
//
boolean SearchLevel::loadNonAdaptSymbols(Sof& sof_a, long tag_a) {

  // if no tag is specified, the tag should be this level number
  //    
  if (tag_a == DEF_TAG) {
    tag_a = (long)level_index_d;
  }
  
  // read the no adapt symbol table if being present in the file
  //  
  if (non_adapt_symbol_table_d.read(sof_a, tag_a, PARAM_NON_ADAPT_SYMBOL)) {
    
    // verify that the no adapt symbols are a subset of all valid symbols
    //
    SearchSymbol symbol;
    for (int i = 0; i < non_adapt_symbol_table_d.length(); i++) {
      symbol.assign(non_adapt_symbol_table_d(i));
      if (!isValidSymbol(getSymbolIndex(symbol))) {
	symbol.debug(L"invalid non adaptation symbol");
	return Error::handle(name(), L"loadNonAdaptSymbols: there is a non adapt symbol that does not appear in the symbol table", SearchLevel::ERR, __FILE__, __LINE__);	
      }
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: storeNonAdaptSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method writes the dummy symbol table from Sof file
//
boolean SearchLevel::storeNonAdaptSymbols(Sof& sof_a, long tag_a) {

  // if no tag is specified, the tag should be this level number
  //  
  if (tag_a == DEF_TAG) {
    tag_a = (long)level_index_d;
  }
  
  // store the symbol table and exit
  //
  return non_adapt_symbol_table_d.write(sof_a, tag_a,
					PARAM_NON_ADAPT_SYMBOL);
}

// method: loadExcludeSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method read the exclude symbol table from Sof file
//
boolean SearchLevel::loadExcludeSymbols(Sof& sof_a, long tag_a) {

  // if no tag is specified, the tag should be this level number
  //    
  if (tag_a == DEF_TAG) {
    tag_a = (long)level_index_d;
  }
  
  // read the exclude symbol table if being present in the file
  //  
  if (exclude_symbol_table_d.read(sof_a, tag_a, PARAM_EXCLUDE_SYMBOL)) {
    
    // verify that the exclude symbols are a subset of all valid symbols
    //
    SearchSymbol symbol;
    for (int i = 0; i < exclude_symbol_table_d.length(); i++) {
      symbol.assign(exclude_symbol_table_d(i));
      if (!isValidSymbol(getSymbolIndex(symbol))) {
	symbol.debug(L"invalid exclude symbol");
	return Error::handle(name(), L"loadExcludeSymbols: there is a exclude symbol that does not appear in the symbol table", SearchLevel::ERR, __FILE__, __LINE__);	
      }
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: storeExcludeSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method writes the exclude symbol table from Sof file
//
boolean SearchLevel::storeExcludeSymbols(Sof& sof_a, long tag_a) {

  // if no tag is specified, the tag should be this level number
  //  
  if (tag_a == DEF_TAG) {
    tag_a = (long)level_index_d;
  }
  
  // store the symbol table and exit
  //
  return exclude_symbol_table_d.write(sof_a, tag_a, PARAM_EXCLUDE_SYMBOL);
}

// method: loadNSymbolExcludeSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method read the nsymbol exclude symbol table from Sof file
//
boolean SearchLevel::loadNSymbolExcludeSymbols(Sof& sof_a, long tag_a) {

  // if no tag is specified, the tag should be this level number
  //    
  if (tag_a == DEF_TAG) {
    tag_a = (long)level_index_d;
  }
  
  // read the nsymbol exclude symbol table if being present in the file
  //  
  if (nsymbol_exclude_symbol_table_d.read(sof_a, tag_a, PARAM_NSYMBOL_EXCLUDE_SYMBOL)) {
    
    // verify that the nsymbol exclude symbols are a subset of all valid symbols
    //
    SearchSymbol symbol;
    for (int i = 0; i < nsymbol_exclude_symbol_table_d.length(); i++) {
      symbol.assign(nsymbol_exclude_symbol_table_d(i));
      if (!isValidSymbol(getSymbolIndex(symbol))) {
	symbol.debug(L"invalid nsymbol exclude symbol");
	return Error::handle(name(), L"loadExcludeSymbols: there is a nsymbol exclude symbol that does not appear in the symbol table", SearchLevel::ERR, __FILE__, __LINE__);	
      }
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: storeNSymbolExcludeSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method writes the nsymbol exclude symbol table from Sof file
//
boolean SearchLevel::storeNSymbolExcludeSymbols(Sof& sof_a, long tag_a) {

  // if no tag is specified, the tag should be this level number
  //  
  if (tag_a == DEF_TAG) {
    tag_a = (long)level_index_d;
  }
  
  // store the symbol table and exit
  //
  return nsymbol_exclude_symbol_table_d.write(sof_a, tag_a, PARAM_NSYMBOL_EXCLUDE_SYMBOL);
}

// method: loadSPenaltyExcludeSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method read the spenalty exclude symbol table from Sof file
//
boolean SearchLevel::loadSPenaltyExcludeSymbols(Sof& sof_a, long tag_a) {

  // if no tag is specified, the tag should be this level number
  //    
  if (tag_a == DEF_TAG) {
    tag_a = (long)level_index_d;
  }
  
  // read the spenalty exclude symbol table if being present in the file
  //  
  if (spenalty_exclude_symbol_table_d.read(sof_a, tag_a, PARAM_SPENALTY_EXCLUDE_SYMBOL)) {
    
    // verify that the spenalty exclude symbols are a subset of all valid symbols
    //
    SearchSymbol symbol;
    for (int i = 0; i < spenalty_exclude_symbol_table_d.length(); i++) {
      symbol.assign(spenalty_exclude_symbol_table_d(i));
      if (!isValidSymbol(getSymbolIndex(symbol))) {
	symbol.debug(L"invalid spenalty exclude symbol");
	return Error::handle(name(), L"loadExcludeSymbols: there is a spenalty exclude symbol that does not appear in the symbol table", SearchLevel::ERR, __FILE__, __LINE__);	
      }
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: storeSPenaltyExcludeSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method writes the spenalty exclude symbol table from Sof file
//
boolean SearchLevel::storeSPenaltyExcludeSymbols(Sof& sof_a, long tag_a) {

  // if no tag is specified, the tag should be this level number
  //  
  if (tag_a == DEF_TAG) {
    tag_a = (long)level_index_d;
  }
  
  // store the symbol table and exit
  //
  return spenalty_exclude_symbol_table_d.write(sof_a, tag_a, PARAM_SPENALTY_EXCLUDE_SYMBOL);
}

// method: loadLevelTag
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method read the symbol table from Sof file
//
boolean SearchLevel::loadLevelTag(Sof& sof_a, long tag_a) {

  // if no tag is specified, the tag should be this level number
  //    
  if (tag_a == DEF_TAG) {
    tag_a = (long)level_index_d;
  }
  
  // load the level tag
  //
  level_tag_d.read(sof_a, tag_a, PARAM_LEVEL_TAG);

  // exit gracefully
  //
  return true;
}

// method: storeLevelTag
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method writes the Skip symbol table from Sof file
//
boolean SearchLevel::storeLevelTag(Sof& sof_a, long tag_a) {

  // if no tag is specified, the tag should be this level number
  //  
  if (tag_a == DEF_TAG) {
    tag_a = (long)level_index_d;
  }
  
  // store the symbol table and exit
  //
  return level_tag_d.write(sof_a, tag_a, PARAM_LEVEL_TAG);
}

// method: loadSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method read the symbol table from Sof file
//
boolean SearchLevel::loadSymbols(Sof& sof_a, long tag_a) {

  // if no tag is specified, the tag should be this level number
  //    
  if (tag_a == DEF_TAG) {
    tag_a = (long)level_index_d;
  }
  
  // load the symbol table and exit
  //
  symbol_table_d.read(sof_a, tag_a, PARAM_SYMBOL);

  // load the nsymbol model if applicable
  //
  if (use_nsymbol_d) {
    
    // open the n-symbol model file
    //
    Sof tmp_file;
    if (!tmp_file.open(nsymbol_file_d)) {
      return Error::handle(name(), L"loadSymbols", Error::ARG, __FILE__, __LINE__);
    }

    // load the n-symbol model
    //
    if (!loadNSymbolModel(tmp_file, tag_a)) {
      return Error::handle(name(), L"loadSymbols", Error::ARG, __FILE__, __LINE__);
    }
    tmp_file.close();
  }

  // exit gracefully
  //
  return true;
}

// method: loadNSymbolModel
//
// arguments:
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method read the nsymbol model from Sof file
//
boolean SearchLevel::loadNSymbolModel(long tag_a) {

  // if no tag is specified, the tag should be this level number
  //    
  if (tag_a == DEF_TAG) {
    tag_a = (long)level_index_d;
  }
  
  // load the nsymbol model if applicable
  //
  if (use_nsymbol_d) {
    
    // open the n-symbol model file
    //
    Sof tmp_file;
    if (!tmp_file.open(nsymbol_file_d)) {
      return Error::handle(name(), L"loadNSymbolModel", Error::ARG, __FILE__, __LINE__);
    }

    // load the n-symbol model
    //
    if (!loadNSymbolModel(tmp_file, tag_a)) {
      return Error::handle(name(), L"loadNSymbolModel", Error::ARG, __FILE__, __LINE__);
    }
    tmp_file.close();
  }

  // exit gracefully
  //
  return true;
}

// method: storeSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method writes the symbol table from Sof file
//
boolean SearchLevel::storeSymbols(Sof& sof_a, long tag_a) {

  // if no tag is specified, the tag should be this level number
  //    
  if (tag_a == DEF_TAG) {
    tag_a = (long)level_index_d;
  }

  // remove the NO_LEFT_CONTEXT and NO_RIGHT_CONTEXT symbols in case
  // these symbols exist in the symbol-table
  //
  long j = 0;
  long len = symbol_table_d.length();

  Vector<SearchSymbol> symbol_table(len);  
  for (long i = 0; i < len; i++) {
    if (!((symbol_table_d(i).eq(SearchSymbol::NO_LEFT_CONTEXT)) ||
	(symbol_table_d(i).eq(SearchSymbol::NO_RIGHT_CONTEXT)))) {
      symbol_table.setLength(j + 1);
      symbol_table(j) = symbol_table_d(i);
      j++;
    }
  }

  symbol_table_d.assign(symbol_table);
  
  // store the symbol table and exit
  //
  return symbol_table_d.write(sof_a, tag_a, PARAM_SYMBOL);
}


// method: loadNonSpeechSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method read the non-speechsymbol table from Sof file
//
boolean SearchLevel::loadNonSpeechSymbols(Sof& sof_a, long tag_a) {

  // if no tag is specified, the tag should be this level number
  //    
  if (tag_a == DEF_TAG) {
    tag_a = (long)level_index_d;
  }
  
  // read the non-speech boundary symbol table if being present in the file
  //  
  if (nonspeech_boundary_symbol_table_d.read(sof_a, tag_a, PARAM_NONSPEECH_BOUNDARY_SYMBOL)) {
    
    // verify that the non-speech symbols are a subset of all valid symbols
    //
    SearchSymbol symbol;
    for (int i=0; i < nonspeech_boundary_symbol_table_d.length(); i++) {
      symbol.assign(nonspeech_boundary_symbol_table_d(i));
      if (!isValidSymbol(getSymbolIndex(symbol))) {
	symbol.debug(L"invalid non-speech boundary symbol");
	return Error::handle(name(), L"loadNonSpeechSymbols: there is a non-speech boundary symbol that does not appear in the symbol table", SearchLevel::ERR, __FILE__, __LINE__);	
      }
    }
  }
  
  // read the non-speech internal symbol table if being present in the file
  //  
  if (nonspeech_internal_symbol_table_d.read(sof_a, tag_a, PARAM_NONSPEECH_INTERNAL_SYMBOL)) {
    
    // verify that the non-speech symbols are a subset of all valid symbols
    //
    SearchSymbol symbol;
    for (int i=0; i < nonspeech_internal_symbol_table_d.length(); i++) {
      symbol.assign(nonspeech_internal_symbol_table_d(i));
      if (!isValidSymbol(getSymbolIndex(symbol))) {
	symbol.debug(L"invalid non-speech internal symbol");
	return Error::handle(name(), L"loadNonSpeechSymbols: there is a non-speech internal symbol that does not appear in the symbol table", SearchLevel::ERR, __FILE__, __LINE__);	
      }
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: storeNonSpeechSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method writes the non-speech symbol table from Sof file
//
boolean SearchLevel::storeNonSpeechSymbols(Sof& sof_a, long tag_a) {

  // declare local variables
  //
  boolean status = false;
  
  // if no tag is specified, the tag should be this level number
  //  
  if (tag_a == DEF_TAG) {
    tag_a = (long)level_index_d;
  }
  
  // store the symbol tables
  //
  if (nonspeech_boundary_symbol_table_d.length() > 0) {
    status = nonspeech_boundary_symbol_table_d.write(sof_a, tag_a, PARAM_NONSPEECH_BOUNDARY_SYMBOL);
  }
  
  if (nonspeech_internal_symbol_table_d.length() > 0) {
    status &= nonspeech_internal_symbol_table_d.write(sof_a, tag_a, PARAM_NONSPEECH_INTERNAL_SYMBOL);
  }
  
  // exit gracefully
  //
  return status;
}

// method: loadSubGraphs
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method has the object read itself from an Sof file
//
boolean SearchLevel::loadSubGraphs(Sof& sof_a, long tag_a) {

  // make sure the symbol table has been loaded
  //
  long num_symbols = symbol_table_d.length();
  long symbol_index = -1;
  
  if (num_symbols == 0) {
    return Error::handle(name(), L"loadSubGraphs - symbol table has not been loaded", SearchLevel::ERR, __FILE__, __LINE__);
  }
  
  // if no tag is specified, the tag should be its level number
  //
  if (tag_a == DEF_TAG) {
    tag_a = (long)level_index_d;
  }
  
  // declare a temporary holder for the Graph<Ulong>  and read them from file
  // 
  Vector< DiGraph<Ulong> > tmp_graphs;
  if (!tmp_graphs_d.read(sof_a, tag_a, PARAM_MODEL)) {
    return false;
  }

  tmp_graphs.assign(tmp_graphs_d);
  
  // variable for graphs conversion
  //
  long num_vertices;
  GraphVertex<SearchNode>** snode_vert = (GraphVertex<SearchNode>**)NULL;
  SearchNode** snode = (SearchNode**)NULL;
  
  // set the number of sub graphs and allocation mode
  //
  long num_graphs = tmp_graphs.length();
  setNumSubGraphs(num_graphs);

  // convert each Graph<Ulong> to Graph<SearchNode> >
  //
  for (long k = 0; k < num_graphs; k++) {
    
    // get the number of vertices from the input graph
    //
    num_vertices  = tmp_graphs(k).length() + 2;
    
    // declare a graph of SearchNode and a local holder of those temporary
    // GraphVertex<Ulong>
    //
    snode_vert = new GraphVertex<SearchNode>*[num_vertices];
    snode = new SearchNode*[num_vertices];
    GraphVertex<Ulong>* tmp_vert[num_vertices];
    
    // set the properties of graphs
    //
    sub_graphs_d(k).setWeighted(tmp_graphs(k).isWeighted());

    // index all the vertices so that they can be referenced easily
    //
    tmp_vert[0] = tmp_graphs(k).getStart();
    
    // loop through the list of vertices and index them
    //
    tmp_graphs(k).gotoFirst();
    for (long i = 1; i < num_vertices - 1; i++) {
      tmp_vert[i] = const_cast<GraphVertex<Ulong>* >(tmp_graphs(k).getCurr());
      tmp_graphs(k).gotoNext();
    }
    
    // the last vertex is the terminal node
    //
    tmp_vert[num_vertices - 1] = tmp_graphs(k).getTerm();

    // create a list of SearchNode Vertices
    // set the items properly
    //
    snode_vert[0] = sub_graphs_d(k).getStart();
    snode_vert[num_vertices - 1] = sub_graphs_d(k).getTerm();
    
    for (long i = 1; i < num_vertices - 1; i++) {

      // set its item
      //
      Ulong* tmp_item = tmp_vert[i]->getItem();
      snode[i] = new SearchNode();
      snode[i]->setSearchLevel(this);

      symbol_index = (long)(*tmp_item);

      // check if this index is in the range of symbols
      //
      if (symbol_index < 0 || symbol_index >= num_symbols ) {
	return Error::handle(name(), L"loadSubGraphs:: invalid symbol index",
			     SearchLevel::ERR, __FILE__, __LINE__);
      }
      snode[i]->setSymbolId(symbol_index);

    }
    
    // add the vertices to the graph
    //
    for (long i = 1; i < num_vertices - 1; i++) {
      snode_vert[i] = sub_graphs_d(k).insertVertex(snode[i]);
    }

    // connect the vertices with arcs to complete the graph
    //
    for (long i = 0; i < num_vertices; i++) {

      // get the vertex
      //
      GraphVertex<Ulong>* tmp_start_vert = tmp_vert[i];

      // get the list of arcs for this vertex
      //
      boolean arcs_remain = tmp_start_vert->gotoFirst();
      while (arcs_remain) {

	// declare temporary arcs and vertices
	//
	long dest_index = -1;
	GraphArc<Ulong>* tmp_arc = tmp_start_vert->getCurr();
	GraphVertex<Ulong>* dest_vertex = tmp_arc->getVertex();
	boolean is_eps = tmp_arc->getEpsilon();
	
	// find the index of the destination vertex
	//
	for (long j = 0; j < num_vertices; j++) {
	  if (dest_vertex == tmp_vert[j]) {
	    dest_index = j;
	    break;
	  } 
	}

	if (dest_index == -1) {
	  return Error::handle(name(), L"loadSubGraphs -- Vertex not in list of Vertices", SearchLevel::ERR, __FILE__, __LINE__);
	}
	
	// connect the vertices in the searchnode graph
	//
	sub_graphs_d(k).insertArc(snode_vert[i], snode_vert[dest_index],
				  is_eps,
				  (double)tmp_arc->getWeight());
	
	// goto the next arc
	//
	arcs_remain = tmp_start_vert->gotoNext();
      }
    }
    
    // finally check if the number of vertices are same in the converted graph
    //
    if (sub_graphs_d(k).length() != tmp_graphs(k).length()) {

      return Error::handle(name(), L"loadSubGraphs:: different number of vertices after conversion", SearchLevel::ERR, __FILE__, __LINE__);
    }
    
    // clean memory created by the tmp_graphs
    //
    tmp_graphs(k).clear(Integral::FREE);
    delete [] snode_vert;
    delete [] snode;
    
  }  // end convert all the sub_graphs
  
  // exit gracefully
  //
  return true;
}

// method: storeSubGraphs
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method has the object write itself from an Sof file
//
boolean SearchLevel::storeSubGraphs(Sof& sof_a, long tag_a) {

  // make sure the symbol table has been loaded
  //
  long index = 0;
  long dst_index = 0;
  boolean is_eps = false;  
  long num_graphs = sub_graphs_d.length();
  long num_symbols = symbol_table_d.length();
  
  if (num_symbols == 0) {

    // please load symbol table first
    //
    return Error::handle(name(), L"storeSubGraphs", SearchLevel::ERR,
			 __FILE__, __LINE__);
  }
  
  // if no tag is specified, the tag should be its level number
  //
  if (tag_a == DEF_TAG) {
    tag_a = (long)level_index_d;
  }

  // declare a Vector of Graph<Ulong> to be written to file
  //
  Ulong tmp_item;
  SearchNode* snode;  
  GraphArc<SearchNode>* dst_arc;
  GraphVertex<SearchNode>* dst_vertex;  
  Vector< DiGraph<Ulong> > tmp_graphs;
  
  // convert each Graph<SearchNode> > to Graph<Ulong>
  //
  tmp_graphs.setLength(sub_graphs_d.length());
  for (long k = 0; k < num_graphs; k++) {

    // set the properties of graphs
    //
    long num_vertices = sub_graphs_d(k).length() + 2;
    tmp_graphs(k).setWeighted(sub_graphs_d(k).isWeighted());

    // insert all vertices corresponding the current graph index
    //
    index = 0;
    GraphVertex<SearchNode>* vertices[num_vertices];
    GraphVertex<Ulong>* tmp_vertices[num_vertices];

    // add the start vertices to the arrays
    //
    vertices[index] = sub_graphs_d(k).getStart();
    tmp_vertices[index++] = tmp_graphs(k).getStart();

    // add the rest of the vertices to the arrays
    //
    for (boolean more = sub_graphs_d(k).gotoFirst(); more;
	 more = sub_graphs_d(k).gotoNext()) {

      // get the current vertex
      //
      vertices[index] = const_cast<GraphVertex<SearchNode>* >(sub_graphs_d(k).getCurr());

      // get the search node corresponding to the vertex
      //
      snode = vertices[index]->getItem();

      // insert a vertex in the Graph<Ulong> for the current vertex
      // using the symbol ID of the vertex as the node item
      //
      tmp_item.assign(snode->getSymbolId());
      tmp_vertices[index++] = tmp_graphs(k).insertVertex(&tmp_item);
    }

    // add the term vertices to the arrays
    //
    vertices[num_vertices - 1] = sub_graphs_d(k).getTerm();
    tmp_vertices[num_vertices - 1] = tmp_graphs(k).getTerm();
    
    // connect all vertices corresponding to the current graph index
    //
    for (long i = 0; i < num_vertices; i++) {
      boolean arcs_remain = vertices[i]->gotoFirst();
      while (arcs_remain) {

	// get the destination vertex
	//
	dst_arc = vertices[i]->getCurr();
	dst_vertex = dst_arc->getVertex();
	is_eps = dst_arc->getEpsilon();
	
	// find the destination vertex
	//
	dst_index = -1;
	for (long j = 0; j < num_vertices; j++) {
	  if (dst_vertex == vertices[j]) {
	    dst_index = j;
	    break;
	  } 	  
	}

	// error - when we cannot find a destination index
	//
	if (dst_index == -1) {
	  return Error::handle(name(), L"storeSubGraphs", Error::ARG, __FILE__, __LINE__);
	}

	// connect the vertices in the Graph<Ulong>
	//
	if (!tmp_graphs(k).insertArc(tmp_vertices[i], tmp_vertices[dst_index],
				     is_eps, (double)dst_arc->getWeight())) {
	  return Error::handle(name(), L"storeSubGraphs", Error::ARG, __FILE__, __LINE__);
	}
	arcs_remain = vertices[i]->gotoNext();
      }
    }
  }

  // write the Vector of Graph<Ulong> to file
  //
  if (!tmp_graphs.write(sof_a, tag_a, PARAM_MODEL)) {
    return false;
  }
  
  // exit gracefully
  //
  return true;  
}

// method: load
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method has the object read itself from an Sof file
//
boolean SearchLevel::load(Sof& sof_a, long tag_a) {

  // load the level tag
  //
  if (!loadLevelTag(sof_a, tag_a)) {
    return false;
  }

  if (level_tag_d.length() == 0 ) {
    return Error::handle(name(), L"read - level tag is not specified",
			 Error::ARG, __FILE__, __LINE__);
  }
  
  // must load the symbol table first
  //
  if (!loadSymbols(sof_a, tag_a)) {
    return false;
  }

  // load the non-speech symbol table (optional)
  //
  loadNonSpeechSymbols(sof_a, tag_a);

  // load the dummy symbol table (optional)
  //
  loadDummySymbols(sof_a, tag_a);

  // load the exclude symbol table (optional)
  //
  loadExcludeSymbols(sof_a, tag_a);

  // load the nsymbol exclude symbol table (optional)
  //
  loadNSymbolExcludeSymbols(sof_a, tag_a);

  // load the spenalty exclude symbol table (optional)
  //
  loadSPenaltyExcludeSymbols(sof_a, tag_a);    
  
  // load the context less symbol table (optional)
  //
  loadContextLessSymbols(sof_a, tag_a);

  // load the skip symbol table (optional)
  //
  loadSkipSymbols(sof_a, tag_a);  

  // load the non adapt symbol table (optional)
  //
  loadNonAdaptSymbols(sof_a, tag_a);
  
  // load the sub graphs
  //
  if (!loadSubGraphs(sof_a, tag_a)) {
    return false;
  }

  // load the context mapping table (optional)
  //
  loadContextMapping(sof_a, tag_a);
  
  // exit gracefully
  //
  return true;
}

// method: store
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method has the object read itself from an Sof file
//
boolean SearchLevel::store(Sof& sof_a, long tag_a) {

  // store the level tag
  //
  if (!storeLevelTag(sof_a, tag_a)) {
    return false;
  }
  
  // must store the symbol table first
  //
  if (!storeSymbols(sof_a, tag_a)) {
    return false;
  }

  // store the non-speech symbols (optional)
  //
  if ((nonspeech_boundary_symbol_table_d.length() > 0) ||
      (nonspeech_internal_symbol_table_d.length() > 0)) {
    storeNonSpeechSymbols(sof_a, tag_a);
  }
  
  // store the dummy symbols (optional)
  //
  if (dummy_symbol_table_d.length() > 0) {
    storeDummySymbols(sof_a, tag_a);
  }
  
  // store the exclude symbols (optional)
  //
  if (exclude_symbol_table_d.length() > 0) {
    storeExcludeSymbols(sof_a, tag_a);
  }

  // store the nsymbol exclude symbols (optional)
  //
  if (nsymbol_exclude_symbol_table_d.length() > 0) {
    storeNSymbolExcludeSymbols(sof_a, tag_a);
  }

  // store the spenalty exclude symbols (optional)
  //
  if (spenalty_exclude_symbol_table_d.length() > 0) {
    storeSPenaltyExcludeSymbols(sof_a, tag_a);
  }    
  
  // store the context less symbols (optional)
  //
  if (contextless_symbol_table_d.length() > 0) {
    storeContextLessSymbols(sof_a, tag_a);
  }

  // store the skip symbols (optional)
  //
  if (skip_symbol_table_d.length() > 0) {
    storeSkipSymbols(sof_a, tag_a);
  }    

  // store the non adaptation symbols (optional)
  //
  if (non_adapt_symbol_table_d.length() > 0) {
    storeNonAdaptSymbols(sof_a, tag_a);
  }
  
  // store the sub graphs
  //
  if (!storeSubGraphs(sof_a, tag_a)) {
    return false;
  }

  // store the context mapping table (optional)
  //
  if (context_map_d.length() > 0) {
    storeContextMapping(sof_a, tag_a);
  }

  // exit gracefully
  //
  return true;
}

// method: loadOccupancies
//
// arguments: none
//
// return: a boolean indicating status
//
// this method reads the occupancies from the symbol occupancy file
//
boolean SearchLevel::loadOccupancies() {  

  // read the symbol occupancy (optional)
  //
  if (symbol_occupancy_file_d.length() > 0) {
    
    // open the configuration file
    //
    Sof symbol_occupancy_file_sof;    
    if (!symbol_occupancy_file_sof.open(symbol_occupancy_file_d,
					File::READ_ONLY)) {
      return Error::handle(name(), L"read - unable to open occupancy file",
			   Error::ARG, __FILE__, __LINE__);
    }
    
    // make sure there are valid statistical models at this level
    //
    if (stat_models_d.length() == 0) {
      Error::handle(name(), L"loadOccupancies - no statistical models are present at the current level", Error::ARG, __FILE__, __LINE__);
    }
    
    // loop over each statistical model and retrieve their accumulators
    //
    for (long i=0; i < stat_models_d.length(); i++) {
      
      // read the accumulators corresponding to the current model
      //
      stat_models_d(i).readOccupancies(symbol_occupancy_file_sof, (long)i);
    }
    
    // close the occupancy files
    //
    symbol_occupancy_file_sof.close();        
  }
  
  // exit gracefully
  //
  return true;  
}

// method: storeOccupancies
//
// arguments:
//  none
//
// return: a boolean indicating status
//
// this method reads the occupancies from accumulators file
//
boolean SearchLevel::storeOccupancies() {
  
  // write the symbol occupancy (optional)
  //
  if ((symbol_occupancy_file_d.length() > 0) && (write_symbol_occupancy_d)) {
    
    // open the configuration file
    //
    Sof symbol_occupancy_file_sof;    
    if (!symbol_occupancy_file_sof.open(symbol_occupancy_file_d,
					File::WRITE_ONLY)) {
      return Error::handle(name(), L"read - unable to open occupancy file",
			   Error::ARG, __FILE__, __LINE__);
    }
    
    // make sure there are valid statistical models at this level
    //
    if (stat_models_d.length() == 0) {
      Error::handle(name(), L"storeOccupancies - no statistical models are present at the current level", Error::ARG, __FILE__, __LINE__);
    }
    
    // loop over each statistical model and write the occupancies
    //
    for (long i = 0; i < stat_models_d.length(); i++) {
      
      // write the accumulators corresponding to the current model
      //
      stat_models_d(i).writeOccupancies(symbol_occupancy_file_sof, (long)i);
    }
  
    // close the occupancy files
    //
    symbol_occupancy_file_sof.close();        
  }
  
  // exit gracefully
  //
  return true;  
}

// method: eq
//
// arguments:
//  const SearchLevel& compare_level: (input) level to compare
//
// return: true if the levels are equivalent, else false
//
// compare two levels. they are equivalent if they contain equivalent
// constituent objects
//
boolean SearchLevel::eq(const SearchLevel& compare_level_a) const {

  // compare all data
  //
  return (use_beam_prune_d.eq(compare_level_a.use_beam_prune_d) &&
	  beam_threshold_d.almostEqual(compare_level_a.beam_threshold_d) &&
	  use_instance_prune_d.eq(compare_level_a.use_instance_prune_d) &&
	  instance_threshold_d.eq(compare_level_a.instance_threshold_d) &&
	  lm_scale_d.almostEqual(compare_level_a.lm_scale_d) &&
	  symbol_penalty_d.almostEqual(compare_level_a.symbol_penalty_d) &&
	  use_context_d.eq(compare_level_a.use_context_d) &&
	  use_lexical_tree_d.eq(compare_level_a.use_lexical_tree_d) &&
	  left_context_d.eq(compare_level_a.left_context_d) &&
	  right_context_d.eq(compare_level_a.right_context_d) &&
	  context_hash_d.eq(compare_level_a.context_hash_d) &&	  
	  use_nsymbol_d.eq(compare_level_a.use_nsymbol_d) &&
	  nsymbol_model_d.eq(compare_level_a.nsymbol_model_d) &&	  
	  nsymbol_order_d.eq(compare_level_a.nsymbol_order_d) &&
	  write_symbol_occupancy_d.eq
	  (compare_level_a.write_symbol_occupancy_d) &&
	  symbol_occupancy_file_d.eq(compare_level_a.symbol_occupancy_file_d));
}
