// file: $isip/class/search/SymbolGraph/sgrp_05.cc
// version: $Id: sgrp_05.cc 9346 2003-11-23 16:35:10Z alphonso $
//

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

// method: eq
//
// arguments:
//  const SymbolGraph& obj: (input) inpt object to compare
//
// return: a boolean indicating status
//
// this method compares the input object to this object
//
boolean SymbolGraph::eq(const SymbolGraph& graph_a) const {

  // declare local variables
  //
  Vector<Long> in_nodes;
  Vector<Triple<Long, Long, String> > in_symbols;
  Vector<Triple<Pair<Long, Long>, Float, Float> > in_arcs;
  
  Vector<Long> this_nodes;
  Vector<Triple<Long, Long, String> > this_symbols;
  Vector<Triple<Pair<Long, Long>, Float, Float> > this_arcs;

  // compare the scale factors
  //
  if (!scale_d.eq(graph_a.scale_d)) {
    return false;
  }
  
  // compare the symbol penalties
  //  
  if (!penalty_d.eq(graph_a.penalty_d)) {
    return false;
  }
  
  // extract the structure of the current list
  //
  const_cast<SymbolGraph* >(this)->get(this_nodes, this_symbols, this_arcs);
  
  // extract the structure of the input list
  //  
  const_cast<SymbolGraph& >(graph_a).get(in_nodes, in_symbols, in_arcs);

  // compare the nodes lists for quality
  //
  if (!this_nodes.eq(in_nodes)) {
    return false;
  }

  // compare the symbols lists for quality
  //  
  if (!this_symbols.eq(in_symbols)) {
    return false;
  }

  // compare the arcs lists for quality
  //  
  if (this_arcs.length() != in_arcs.length()) {
    return false;
  }

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

    // compare the start index
    //
    if (!this_arcs(i).first().first().eq(in_arcs(i).first().first())) {
      return false;
    }

    // compare the stop index
    //
    if (!this_arcs(i).first().second().eq(in_arcs(i).first().second())) {
      return false;
    }

    // compare the language model score
    //
    if (!Integral::almostEqual((double)this_arcs(i).second(),
			       (double)in_arcs(i).second())) {
      return false;
    }

    // compare the acoustic model score
    //
    if (!Integral::almostEqual((double)this_arcs(i).third(),
			       (double)in_arcs(i).third())) {
      return false;
    }            
  }
  
  // we have reached this far so the graphs have to be equal
  //
  return true;  
}

// method: convert
//
// arguments:
//  SearchLevel& slevel: (input) search level
//  DiGraph<SearchNode>& digraph: (output) digraph representation
//
// return: a boolean indicating status
//
// this method converts the symbol graph to a digraph representation
//
boolean SymbolGraph::convert(SearchLevel& level_a,
			     DiGraph<SearchNode>& digraph_a) {

  // declare local variables
  //
  SearchSymbol input_symbol;
  SearchNode search_node;  
  Queue<SymbolGraphNode> queue;  

  HashKey<SymbolGraphNode> hashkey;
  HashTable<HashKey<SymbolGraphNode>, GraphVertex<SearchNode> > hashtable;
  
  SymbolGraphNode* child_node = (SymbolGraphNode*)NULL;
  SymbolGraphNode* parent_node = (SymbolGraphNode*)NULL;  

  GraphArc<SearchNode>* graph_arc = (GraphArc<SearchNode>*)NULL;
  GraphVertex<SearchNode>* child_vertex = (GraphVertex<SearchNode>*)NULL;  
  GraphVertex<SearchNode>* parent_vertex = (GraphVertex<SearchNode>*)NULL;
  
  // do we need to do anything?
  //
  if ((long)num_nodes_d < 1) {
    return true;
  }

  // set the allocation mode
  //
  queue.setAllocationMode(DstrBase::USER);  
  hashtable.setAllocationMode(DstrBase::USER);

  // insert the start node into the queue
  //
  queue.add(&start_node_d);
  
  hashkey.assign(&start_node_d);
  hashtable.insert(hashkey, digraph_a.getStart());
  
  while (!queue.isEmpty()) {
    
    // retrieve the symbol graph start node
    //
    parent_node = queue.remove();

    if (parent_node == (SymbolGraphNode*)NULL) {
      return Error::handle(name(), L"convertSymbolGraph", Error::ARG, __FILE__, __LINE__);
    }
    
    // retrieve the digraph start vertex
    //
    hashkey.assign(parent_node);
    
    if ((parent_vertex = hashtable.get(hashkey)) ==
	(GraphVertex<SearchNode>*)NULL) {
      return Error::handle(name(), L"convertSymbolGraph", Error::ARG, __FILE__, __LINE__);
    }
    
    // get the next child nodes and scores corresponding to the parent
    //  
    SingleLinkedList<SymbolGraphNode>& next_nodes = parent_node->getNextNodesList();
    SingleLinkedList<Float>& lm_scores = parent_node->getLMScoresList();
    SingleLinkedList<Float>& ac_scores = parent_node->getScoresList();
    
    // loop over all nodes adjacent to the start node
    //
    lm_scores.gotoFirst();
    ac_scores.gotoFirst();
    
    for (boolean more = next_nodes.gotoFirst(); more;
	 more = next_nodes.gotoNext()) {
      
      // retrieve the next child node
      //
      child_node = next_nodes.getCurr();

      if (child_node == (SymbolGraphNode*)NULL) {
	return Error::handle(name(), L"convertSymbolGraph", Error::ARG, __FILE__, __LINE__);
      }
            
      // get the language model and acoustic model scores
      //
      Float* val = (Float*)NULL;
      if ((val = lm_scores.getCurr()) == (Float*)NULL) {
	return Error::handle(name(), L"convertSymbolGraph", Error::ARG, __FILE__, __LINE__);
      }
      float lm_score = (float)(*val);
      
      val = (Float*)NULL;
      if ((val = ac_scores.getCurr()) == (Float*)NULL) {
	return Error::handle(name(), L"convertSymbolGraph", Error::ARG, __FILE__, __LINE__);
      }      
      float ac_score = (float)(*val);

      // when the child node is not the term node
      //
      if (child_node != &term_node_d) {
	
	// create the corresponding child vertex
	//
	child_node->getSymbol(input_symbol);
	hashkey.assign(child_node);
	
	if ((child_vertex = hashtable.get(hashkey)) ==
	    (GraphVertex<SearchNode>*)NULL) {
	  
	  search_node.setSearchLevel(&level_a);
	  search_node.setSymbol(input_symbol);
	  
	  child_vertex = digraph_a.insertVertex(&search_node);
	  hashtable.insert(hashkey, child_vertex);

	  // add the child node to the queue
	  //
	  queue.add(child_node);
	}
	
	// insert the corresponding transition in the digraph
	//
	digraph_a.insertArc(parent_vertex, child_vertex,
			    graph_arc, false, (float)0.0);
	
	// set the auxiliary arc weights
	//
	graph_arc->setLanguageWeight((double)lm_score);
	graph_arc->setAcousticWeight((double)ac_score);

	// print debugging information
	//
	if (debug_level_d >= Integral::DETAILED) {
	  
	  String src_str;
	  parent_node->getSymbol(src_str);
	  String dst_str;
	  child_node->getSymbol(dst_str);	
	  
	  String output(L"\n-> src: ");
	  output.concat(parent_vertex);
	  output.concat(L" [");
	  output.concat(src_str);	
	  output.concat(L"], dst: ");
	  output.concat(child_vertex);
	  output.concat(L" [");
	  output.concat(dst_str);		
	  output.concat(L"], lm_score: ");
	  output.concat(lm_score);
	  output.concat(L", ac_score: ");
	  output.concat(ac_score);	
	  Console::put(output);
	}	
      }
      
      // when the child node is the term node
      //    
      else {
	
	// create the corresponding child vertex
	//            
	child_vertex = digraph_a.getTerm();

	if (!parent_vertex->isAdjacent(child_vertex)) {

	  // insert the corresponding transition in the digraph
	  //	  
	  digraph_a.insertArc(parent_vertex, child_vertex,
			      graph_arc, false, (float)0.0);

	  graph_arc->setLanguageWeight((double)lm_score);
	  graph_arc->setAcousticWeight((double)ac_score);
	  
	  // print debugging information
	  //
	  if (debug_level_d >= Integral::DETAILED) {
	    
	    String src_str;
	    parent_node->getSymbol(src_str);
	    String dst_str;
	    child_node->getSymbol(dst_str);	
	    
	    String output(L"\n-> src: ");
	    output.concat(parent_vertex);
	    output.concat(L" [");
	    output.concat(src_str);	
	    output.concat(L"], dst: ");
	    output.concat(child_vertex);
	    output.concat(L" [");
	    output.concat(dst_str);		
	    output.concat(L"], lm_score: ");
	    output.concat(lm_score);
	    output.concat(L", ac_score: ");
	    output.concat(ac_score);	
	    Console::put(output);
	  }		  
	}
      }
      
      // move to the next child
      //
      lm_scores.gotoNext();
      ac_scores.gotoNext();    
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: get
//
// arguments:
//  Vector<Long>& nodes: (output) graph nodes
//  Vector<Triple<Long, Long, String> >& symbols: (output) graph symbols
//  Vector<Triple<Pair<Long, Long>, Float, Float> >& arcs: (output) graph arcs
//
// return: a boolean indicating status
//
// this method retrieves the internal structure of the graph
//
boolean SymbolGraph::get(Vector<Long>& nodes_a,
	      Vector<Triple<Long, Long, String> >& symbols_a,
	      Vector<Triple<Pair<Long, Long>, Float, Float> >& arcs_a) {

  // declare local variables
  //
  long num_nodes = 0;
  long num_arcs = 0;

  long start_index = 0;
  long stop_index = 0;
  long frame_index = 0;    
  
  String symbol;  
  Long node_index;

  HashKey<SymbolGraphNode> hashkey;
  HashTable<HashKey<SymbolGraphNode>, Long> node_lookup;
  
  // clear the datastructures to begin with
  //
  nodes_a.clear();
  symbols_a.clear();  
  arcs_a.clear();
  
  // set the default capacity
  //
  nodes_a.setCapacity((long)num_nodes_d + 2);
  arcs_a.setCapacity((long)num_arcs_d + 2);
  symbols_a.setCapacity((long)num_arcs_d + 2);
  
  // update the vector length
  //
  if (nodes_a.length() < num_nodes + 1) {
    nodes_a.setLength(num_nodes + 1);
  }

  // set up the start node
  //
  node_index = 0;
  frame_index = 0;
  
  hashkey.assign(&start_node_d);
  node_lookup.insert(hashkey, &node_index);
    
  nodes_a(num_nodes++) = frame_index;
  node_index++;
    
  // loop over all nodes in the graph and set up the nodes vector
  //
  for (boolean more = nodes_d.gotoFirst(); more; more = nodes_d.gotoNext()) {

    // get the current parent node
    //
    SymbolGraphNode* node = nodes_d.getCurr();

    if (node == (SymbolGraphNode*)NULL) {
      return Error::handle(name(), L"get", Error::ARG, __FILE__, __LINE__);
    }
    
    // get and set the symbol and frame of the current node
    //
    frame_index = node->getFrameIndex();
    
    // update the vector length
    //
    if (nodes_a.length() < num_nodes + 1) {
      nodes_a.setLength(num_nodes + 1);
    }

    hashkey.assign(node);
    node_lookup.insert(hashkey, &node_index);
    
    nodes_a(num_nodes++) = frame_index;
    node_index++;
  }

  // add the children corresponding to the start node
  //
  SingleLinkedList<SymbolGraphNode>& next_nodes = start_node_d.getNextNodesList();
  SingleLinkedList<Float>& lm_scores = start_node_d.getLMScoresList();
  SingleLinkedList<Float>& ac_scores = start_node_d.getScoresList();

  // loop over all children corresponding to the start node
  //
  lm_scores.gotoFirst();
  ac_scores.gotoFirst();

  for (boolean more = next_nodes.gotoFirst(); more;
       more = next_nodes.gotoNext()) {

    SymbolGraphNode* child = next_nodes.getCurr();
    
    if (child == (SymbolGraphNode*)NULL) {
      return Error::handle(name(), L"get", Error::ARG, __FILE__, __LINE__);
    }

    // get the parent start index
    //
    start_index = 0;
  
    // get the child symbol and stop index
    //
    child->getSymbol(symbol);

    Long* ind = (Long*)NULL;
    
    hashkey.assign(child);
    if ((ind = node_lookup.get(hashkey)) == (Long*)NULL) {
      return Error::handle(name(), L"get", Error::ARG, __FILE__, __LINE__);
    }
    
    stop_index = (long)(*ind);

    // skip the term nodes
    //
    if (child != &term_node_d) {
      
      // get the language model and acoustic model scores
      //
      Float* val = (Float*)NULL;
      if ((val = lm_scores.getCurr()) == (Float*)NULL) {
	return Error::handle(name(), L"get", Error::ARG, __FILE__, __LINE__);
      }
      float lm_score = (float)(*val);
      
      val = (Float*)NULL;
      if ((val = ac_scores.getCurr()) == (Float*)NULL) {
	return Error::handle(name(), L"get", Error::ARG, __FILE__, __LINE__);
      }      
      float ac_score = (float)(*val);
      
      // update the vector length
      //
      if (arcs_a.length() < num_arcs + 1) {
	arcs_a.setLength(num_arcs + 1);
	symbols_a.setLength(num_arcs + 1);	
      }

      symbols_a(num_arcs).first().assign(start_index);
      symbols_a(num_arcs).second().assign(stop_index);
      symbols_a(num_arcs).third().assign(symbol);
            
      arcs_a(num_arcs).first().first().assign(start_index);
      arcs_a(num_arcs).first().second().assign(stop_index);
      arcs_a(num_arcs).second().assign(ac_score);      
      arcs_a(num_arcs).third().assign(lm_score);
      
      num_arcs++;
    }
    
    // move to the next child
    //
    lm_scores.gotoNext();
    ac_scores.gotoNext();
  }
  
  // loop over all nodes in the graph and set up the arcs vector
  //
  for (boolean more = nodes_d.gotoFirst(); more; more = nodes_d.gotoNext()) {

    // get the current parent node
    //
    SymbolGraphNode* parent = nodes_d.getCurr();

    if (parent == (SymbolGraphNode*)NULL) {
      return Error::handle(name(), L"get", Error::ARG, __FILE__, __LINE__);
    }

    // get the parent start index
    //
    Long* ind = (Long*)NULL;
    
    hashkey.assign(parent);
    if ((ind = node_lookup.get(hashkey)) == (Long*)NULL) {
      return Error::handle(name(), L"get", Error::ARG, __FILE__, __LINE__);
    }
    
    start_index = (long)(*ind);

    // get the next child nodes and scores corresponding to the parent
    //
    SingleLinkedList<SymbolGraphNode>& next_nodes = parent->getNextNodesList();
    SingleLinkedList<Float>& lm_scores = parent->getLMScoresList();
    SingleLinkedList<Float>& ac_scores = parent->getScoresList();
    
    // loop over all children corresponding to the parent
    //
    lm_scores.gotoFirst();
    ac_scores.gotoFirst();
    
    for (boolean more1 = next_nodes.gotoFirst(); more1;
	 more1 = next_nodes.gotoNext()) {

      SymbolGraphNode* child = next_nodes.getCurr();

      if (child == (SymbolGraphNode*)NULL) {
	return Error::handle(name(), L"get", Error::ARG, __FILE__, __LINE__);
      }

      // get the child symbol and stop index
      //
      child->getSymbol(symbol);
      
      // skip the term nodes
      //
      if (child != &term_node_d) {

	Long* ind = (Long*)NULL;
	
	hashkey.assign(child);
	if ((ind = node_lookup.get(hashkey)) == (Long*)NULL) {
	  return Error::handle(name(), L"get", Error::ARG, __FILE__, __LINE__);
	}
	
	stop_index = (long)(*ind);
	
	// get the language model and acoustic model scores
	//
	Float* val1 = (Float*)NULL;
	if ((val1 = lm_scores.getCurr()) == (Float*)NULL) {
	  return Error::handle(name(), L"get", Error::ARG, __FILE__, __LINE__);
	}
	float lm_score = (float)(*val1);
	
	val1 = (Float*)NULL;
	if ((val1 = ac_scores.getCurr()) == (Float*)NULL) {
	  return Error::handle(name(), L"get", Error::ARG, __FILE__, __LINE__);
	}      
	float ac_score = (float)(*val1);
	
	// update the vector length
	//
	if (arcs_a.length() < num_arcs + 1) {
	  arcs_a.setLength(num_arcs + 1);
	  symbols_a.setLength(num_arcs + 1);	  
	}

	symbols_a(num_arcs).first().assign(start_index);
	symbols_a(num_arcs).second().assign(stop_index);
	symbols_a(num_arcs).third().assign(symbol);
	
	arcs_a(num_arcs).first().first().assign(start_index);
	arcs_a(num_arcs).first().second().assign(stop_index);
	arcs_a(num_arcs).second().assign(ac_score);
	arcs_a(num_arcs).third().assign(lm_score);	
	
	num_arcs++;	
      }
      
      // move to the next child
      //
      lm_scores.gotoNext();
      ac_scores.gotoNext();
    }
  }
  
  // exit gracefully
  //  
  return true;    
}

// method: clear
//
// arguments:
//  Integral::CMODE cmode_a: (input) clear mode
//
// return: a boolean value indicating status
//
// this method clears the reference to the internal data.
// 
boolean SymbolGraph::clear(Integral::CMODE cmode_a) {

  // declare local variables
  //
  SymbolGraphNode* node = (SymbolGraphNode*)NULL;
  
  // reset the member data
  //
  scale_d = DEF_SCALE;
  penalty_d = DEF_PENALTY;
  order_d = DEF_ORDER;
  format_d = DEF_FORMAT;

  // clear the start node contents
  //
  start_node_d.clear(cmode_a);
  
  // clear the term node contents
  //  
  term_node_d.clear(cmode_a);
  
  // removes the nodes from the graph
  //
  while (!nodes_d.isEmpty()) {

    nodes_d.removeFirst(node);

    if (node != (SymbolGraphNode*)NULL) {
      delete node;
      node = (SymbolGraphNode*)NULL;
    }
  }
  
  // exit gracefully
  //  
  return true;      
}

// method: set
//
// arguments:
//  Vector<Long>& nodes: (output) graph nodes
//  Vector<Triple<Long, Long, String> >& symbols: (output) graph symbols
//  Vector<Triple<Pair<Long, Long>, Float, Float> >& arcs: (output) graph arcs
//
// return: a boolean indicating status
//
// this method sets the internal structure of the graph
//
boolean SymbolGraph::set(Vector<Long>& nodes_a,
	      Vector<Triple<Long, Long, String> >& symbols_a,
	      Vector<Triple<Pair<Long, Long>, Float, Float> >& arcs_a) {

  // declare local variables
  //
  long num_arcs = 0;
  long num_nodes = 0;  

  long start_index = 0;
  long stop_index = 0;
  long frame_index = 0;
  
  float ac_score = 0.0;
  float lm_score = 0.0;  
  
  String symbol;

  SymbolGraphNode** nodes = (SymbolGraphNode**)NULL;
  SymbolGraphNode* start_node = (SymbolGraphNode*)NULL;
  SymbolGraphNode* stop_node = (SymbolGraphNode*)NULL;  
  
  // get the number of nodes and arcs
  //
  num_arcs = arcs_a.length();
  num_nodes = nodes_a.length();
  
  if (num_nodes < 1) {
    return false;
  }

  // allocate memory
  //  
  nodes = new SymbolGraphNode*[num_nodes];

  // set the start node -- assumed to be at index zero
  //
  nodes[0] = &start_node_d;
  nodes[0]->setParentGraph(this);
  
  // loop over all nodes and insert them into the graph
  //
  for (long i = 1; i < num_nodes; i++) {

    // get the frame index corresponding to the node
    //
    frame_index = (long)nodes_a(i);

    // insert the node in the graph
    //
    nodes[i] = insertNode(frame_index);
    nodes[i]->setParentGraph(this);    
  }

  // loop over all arcs and insert them into the graph
  //
  for (long i = 0; i < num_arcs; i++) {

    // get the start and stop indices
    //
    start_index = (long)arcs_a(i).first().first();

    if ((start_index < 0) || (start_index > num_nodes - 1)) {
      return Error::handle(name(), L"set", Error::ARG, __FILE__, __LINE__);
    }
    
    stop_index = (long)arcs_a(i).first().second();

    if ((stop_index < 0) || (stop_index > num_nodes - 1)) {
      return Error::handle(name(), L"set", Error::ARG, __FILE__, __LINE__);
    }
    
    // get the start and stop nodes
    //
    start_node = nodes[start_index];

    if (start_node == (SymbolGraphNode*)NULL) {
      return Error::handle(name(), L"set", Error::ARG, __FILE__, __LINE__);
    }
    
    stop_node = nodes[stop_index];

    if (stop_node == (SymbolGraphNode*)NULL) {
      return Error::handle(name(), L"set", Error::ARG, __FILE__, __LINE__);
    }

    // get the stop symbol
    //
    long pind = (long)symbols_a(i).first();
    long cind = (long)symbols_a(i).second();    
    symbol.assign(symbols_a(i).third());

    // the symbol indices must match
    //
    if ((pind != start_index) || (cind != stop_index)) {
      return Error::handle(name(), L"set", Error::ARG, __FILE__, __LINE__);
    }

    stop_node->setSymbol(symbol);
    
    // get the language model and acoustic model scores
    //
    ac_score = (float)arcs_a(i).second();
    lm_score = (float)arcs_a(i).third();

    // insert the transition in the graph
    //
    if (!start_node->insertNextNode(stop_node, lm_score, ac_score)) {
      return Error::handle(name(), L"set", Error::ARG, __FILE__, __LINE__);
    }
  }

  // loop over all nodes in the graph
  //
  for (long i = 0; i < num_nodes; i++) {

    // when there are no outgoing transitions
    //
    if (nodes[i]->getOutgoing() == 0) {

      // connect the node to the term node
      //
      start_node = nodes[i];
      stop_node = &term_node_d;
      
      // insert the transition in the graph
      //
      if (!start_node->insertNextNode(stop_node, 0.0, 0.0)) {
	return Error::handle(name(), L"set", Error::ARG, __FILE__, __LINE__);
      }
    }
  }
  
  // free memory
  //
  delete [] nodes;
  nodes = (SymbolGraphNode**)NULL;
  
  // exit gracefully
  //  
  return true;    
}

// method: compact
//
// arguments:
//   SymbolGraph& new_graph: (output) reduced graph representation
//
// return: a boolean indicating status
//
// this method reduces the size of the symbol graph into a more compact form
//
boolean SymbolGraph::compact(SymbolGraph& new_graph_a) {

  // do we need to do anything?
  //
  if ((long)num_nodes_d < 1) {
    return true;
  }
  
  // initialize the nodes for the new symbol graph
  //
  SymbolGraphNode** new_lnodes = new SymbolGraphNode*[(long)num_nodes_d + 1];
  
  // define a map into the new symbol graph
  //
  long* map_list = new long[(long)num_nodes_d + 1];

  for (long i = 0; i < (long)num_nodes_d + 1; i++) {
    map_list[i] = -1;
    new_lnodes[i] = (SymbolGraphNode*)NULL;
  }

  // clear the input graph before we begin
  //
  new_graph_a.clear();
  
  // set the scale and penalty
  //
  new_graph_a.scale_d = scale_d;
  new_graph_a.penalty_d = penalty_d;
  
  // set the order and format
  //
  new_graph_a.format_d = format_d;
  new_graph_a.order_d = order_d;

  // place the start node at the first position in the new lattice
  // array, update the map and increment count
  //
  new_lnodes[0] = new_graph_a.getStart();
  map_list[start_node_d.getNodeIndex()] = 0;

  // link all descendants of the lattice node
  //
  new_lnodes[0]->insertNode(&start_node_d, new_lnodes, map_list);
  
  // free allocated memory
  //
  delete [] map_list;
  delete [] new_lnodes;
  
  // exit gracefully
  //  
  return true;      
}

// method: prune
//
// arguments: none
//
// return: a boolean indicating status
//
// this method prunes the symbol graph removing all nodes that are
// not referenced
//
boolean SymbolGraph::prune() {

  // declare local variables
  //
  SymbolGraphNode* node = (SymbolGraphNode*)NULL;
  
  // loop over all nodes in the list
  //
  boolean more = nodes_d.gotoFirst();
  while (more) {

    node = nodes_d.getCurr();

    // remove the node if necessary
    //
    if (node->getRefCount() < 1) {

      if (!removeNode(node)) {
	return Error::handle(name(), L"prune", Error::ARG, __FILE__, __LINE__);
      }
      
      more = nodes_d.gotoFirst();      
    }

    else {
      more = nodes_d.gotoNext();
    }
  }
  
  // exit gracefully
  //  
  return true;  
}

// method: insertNode
//
// arguments:
//  long frame: (input) frame index of the node
//  const String& symbol: (input) symbol index of the node
//
// return: a boolean indicating status
//
// this method insert a new node in the symbol graph with the input
// frame and symbol index
//
SymbolGraphNode* SymbolGraph::insertNode(long frame_a,
					 const String& symbol_a) {

  // declear local variables
  //
  SymbolGraphNode* new_node = (SymbolGraphNode*)NULL;
  
  // create the new node to be inserted in the graph
  //
  new_node = new SymbolGraphNode(frame_a, symbol_a);
  
  // set the parent graph to be this
  //
  new_node->setParentGraph(this);

  // append the newly created object to the end of the list
  //
  nodes_d.insertLast(new_node);

  // increment the node counter
  //
  num_nodes_d++;

  // set the node index
  //
  new_node->setNodeIndex(num_nodes_d);

  // return a reference to the object just created
  //
  return new_node;  
}

// method: removeNode
//
// arguments:
//  SymbolGraphNode* obj: (input) node to be removed from the graph
//
// return: a boolean indicating status
//
// this method the specified node from the graph
//
boolean SymbolGraph::removeNode(SymbolGraphNode* obj_a) {

  // declare local variables
  //
  SymbolGraphNode* ptr = (SymbolGraphNode*)NULL;

  // error checking
  //
  if (obj_a == (SymbolGraphNode*)NULL) {
    return Error::handle(name(), L"removeNode", Error::ARG, __FILE__, __LINE__);
  }

  // are any other nodes referencing this node?
  //
  if (obj_a->getRefCount() > 0) {
    return Error::handle(name(), L"removeNode", Error::ARG, __FILE__, __LINE__);
  }
  
  // find the current object in the list
  //
  if (nodes_d.find(obj_a)) {
    
    // decrement the node count
    //
    num_nodes_d--;

    // remove the object from the list
    //
    nodes_d.remove(ptr);
    
    // pointers should match
    //
    if (ptr != obj_a) {
      return Error::handle(name(), L"removeNode", Error::ARG, __FILE__, __LINE__);
    }

    // loop over all nodes in the previous list and decrement their reference
    // counts
    //
    SingleLinkedList<SymbolGraphNode>& prev_list = obj_a->getPrevNodesList();
    
    for (boolean more = prev_list.gotoFirst(); more;
	 more = prev_list.gotoNext()) {

      // decrement the arc count
      //      
      num_arcs_d--;

      // decrement the previous nodes ref count
      //
      prev_list.getCurr()->decrementRefCount();
    }
    
    // delete the object
    //
    delete obj_a;
  }

  // exit gracefully
  //  
  return true;
}
