// file: $isip/class/pr/LanguageModelXML/lmxml_03.cc
// version: $Id: lmxml_03.cc 10356 2006-01-10 18:52:05Z wholland $

// ISIP include files
//
#include "LanguageModelXML.h"

// method: read
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//  const String& name: (input) sof object instance name
//
// return: a boolean value indicating status
//
// this method has the object read itself from an Sof file
//
boolean LanguageModelXML::read(Sof& sof_a, long tag_a, const String& name_a) {

  if (debug_level_d > Integral::BRIEF) {
    Console::put(L"reading model");
    Console::increaseIndention();			  
  }
  
  // read the instance of the object from the Sof file
  //
  if (!sof_a.find(name_a, tag_a)) {
    return false;
  }

  // read the actual data from the sof file
  //
  if (!readData(sof_a)) {
    return false;
  }

  if (debug_level_d > Integral::BRIEF) {
    Console::decreaseIndention();
  }
  
  // exit gracefully
  //
  return true;  
}

// method: readData
//
// arguments:
//  Sof& sof: (input) sof file object
//  const String& pname: (input) parameter name
//  long size: (input) size in bytes of object (or full_size)
//  boolean param: (input) is the parameter name in the file
//  boolean nested: (input) are we nested?
//
// return: a boolean value indicating status
//
// this method has the object read itself from an Sof file. it assumes
// that the Sof file is already positioned correctly.
//
boolean LanguageModelXML::readData(Sof& sof_a, const String& pname_a,
			      long size_a, boolean param_a,
			      boolean nested_a) {

  DebugLevel temp = debug_level_d;
  
  clear(Integral::FREE);

  setDebug(temp);
  
  SofParser parser;

  parser.setDebug(Integral::NONE);
  
  // are we nested?
  //
  if (nested_a) {
    parser.setNest();
  }

  // load the parse
  //
  if (!parser.load(sof_a, size_a)) {
    return Error::handle(name(), L"readData", Error::READ,
			 __FILE__, __LINE__, Error::WARNING);
  }

  Vector<SearchSymbol> temp_vector;
  
  // count the number of levels
  //
  long num_levels = 0;
    
  String level_tag(SearchLevel::PARAM_LEVEL_TAG);
  level_tag.concat(PARAM_UNDERSCORE);
  level_tag.concat((Long)num_levels);
    
  while (parser.isPresent(sof_a, level_tag)) {
    num_levels++;
    level_tag.assign(SearchLevel::PARAM_LEVEL_TAG);
    level_tag.concat(PARAM_UNDERSCORE);
    level_tag.concat((Long)num_levels);
  }

  if (num_levels == 0) {
    return Error::handle(name(), L"readData: incorrect format", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
  }

  hg_d.setLength(num_levels);
  
  if (debug_level_d > Integral::BRIEF) {
    Console::put(L"reading levels");
    Console::increaseIndention();
  }
  
  // loop over levels and read grammar, context mapping, and symbol
  // types for each level
  //
  for (int level = 0; level < num_levels; level++) {

    // set the level index
    //
    hg_d(level).setLevelIndex(level);
    
    boolean context = false;

    if (level > 0
	&& hg_d(level - 1).getContextMap().length() > 0) {
      context = true;
    }

    if (debug_level_d > Integral::BRIEF) {
      Console::put(L"reading grammars");
      Console::increaseIndention();
    }
    
    // read XML grammars
    //
    if (context || level == 0) {
      readXMLGrammars(sof_a, PARAM_GRAMMARS, level, context, NULL, parser);
    }
    else{
      readXMLGrammars(sof_a, PARAM_GRAMMARS, level, context,
		      &(hg_d(level - 1).getSymbolTable()), parser);
    }

    if (debug_level_d > Integral::BRIEF) {
      Console::decreaseIndention();
      Console::put(L"reading level tag");
      Console::increaseIndention();
    }
    
    // read the level tag
    //
    Vector<SearchSymbol> level_tag;
    readSymbolType(sof_a, level, SearchLevel::PARAM_LEVEL_TAG,
		   level_tag,
		   parser);
    hg_d(level).setLevelTag(level_tag(0));

    if (debug_level_d > Integral::BRIEF) {
      Console::decreaseIndention();
      Console::put(L"reading symbols");
      Console::increaseIndention();
    }
    
    // read the nonspeech boundary symbols
    //
    readSymbolType(sof_a, level, SearchLevel::PARAM_NONSPEECH_BOUNDARY_SYMBOL,
		   hg_d(level).getNonSpeechBoundarySymbolTable(),
		   parser);

    // read the nonspeech internal symbols
    //
    readSymbolType(sof_a, level, SearchLevel::PARAM_NONSPEECH_INTERNAL_SYMBOL,
		   hg_d(level).getNonSpeechInternalSymbolTable(),
		   parser);

    // read the dummy symbols
    //
    readSymbolType(sof_a, level, SearchLevel::PARAM_DUMMY_SYMBOL,
		   hg_d(level).getDummySymbolTable(),
		   parser);

    // IF any dummy symbols were created to replace the special rule
    // NULL, AND the default dummy symbol does not exist
    // in the dummy symbols list read from the file, then add it
    //
    SearchSymbol def_dummy_symbol = getDummySymbol();
    if(getSymbolList().contains(&def_dummy_symbol)
       && !hg_d(level).getDummySymbolTable().contains(&def_dummy_symbol)) {
      hg_d(level).getDummySymbolTable().concat(def_dummy_symbol);
    }

    // read the exclude symbols
    //
    readSymbolType(sof_a, level, SearchLevel::PARAM_EXCLUDE_SYMBOL,
		   hg_d(level).getExcludeSymbolTable(),
		   parser);

    // read the nsymbol exclude symbols
    //
    readSymbolType(sof_a, level, SearchLevel::PARAM_NSYMBOL_EXCLUDE_SYMBOL,
		   hg_d(level).getNSymbolExcludeSymbolTable(),
		   parser);

    // read the spenalty exclude symbols
    //
    readSymbolType(sof_a, level, SearchLevel::PARAM_SPENALTY_EXCLUDE_SYMBOL,
		   hg_d(level).getSPenaltyExcludeSymbolTable(),
		   parser);
    
    // read the context less symbols
    //
    readSymbolType(sof_a, level, SearchLevel::PARAM_CONTEXTLESS_SYMBOL,
		   hg_d(level).getContextLessSymbolTable(),
		   parser);

    // read the skip symbols
    //
    readSymbolType(sof_a, level, SearchLevel::PARAM_SKIP_SYMBOL,
		   hg_d(level).getSkipSymbolTable(),
		   parser);

    // read the non adaptation symbols
    //
    readSymbolType(sof_a, level, SearchLevel::PARAM_NON_ADAPT_SYMBOL,
		   hg_d(level).getNonAdaptSymbolTable(),
		   parser);

    if (debug_level_d > Integral::BRIEF) {
      Console::decreaseIndention();
      Console::put(L"reading context mapping");
      Console::increaseIndention();
    }
    
    // read the context mapping (if it exists) for this level
    //
    //
    readContextMapping(sof_a, SearchLevel::PARAM_CONTEXT_MAPPING, level,
		       parser);

    if (debug_level_d > Integral::BRIEF) {
      Console::decreaseIndention();
    }
    
  }

  if (debug_level_d > Integral::BRIEF) {
    Console::decreaseIndention();
  }
  
  // indicate success
  //
  return true;
}

// method: readXMLGrammars
//
// arguments:
//  Sof& sof: (input) sof file object
//  long level: (input) language model level
//  boolean use_context: (input) are we using context?
//  const String& name: (input) parameter name
//  Vector<SearchSymbol> symbol_list: (output) list of symbols
//
// return: a boolean value indicating status
//
// this method reads the grammar for a particular level
//
boolean LanguageModelXML::readXMLGrammars(Sof& sof_a, const String& name_a,
					long level_a, boolean use_context_a,
					Vector<SearchSymbol>* symbol_table_a,
					SofParser& parser_a) {
  
  // symbol list for the given search level
  //
  Vector<SearchSymbol> symbol_list;
  Vector< Vector<XMLToken> > grammar_list;
  Vector<String> grammar_name_list;
  Vector<String> grammars;
  
  String param_name(name_a);
  param_name.concat(PARAM_UNDERSCORE);
  param_name.concat((Long)level_a);

  // read the grammar string vector
  //
  if (parser_a.isPresent(sof_a, param_name)) {
    if (!grammars.readData(sof_a, param_name, parser_a.getEntry(sof_a,
								param_name),
			   false, false)) {
      return Error::handle(name(), L"readData: error reading grammars",
			   Error::READ, __FILE__, __LINE__, Error::WARNING);
    }
  }  
  
  for (int i = 0; i < grammars.length(); i++) {

    // declare variables
    //
    Vector<SearchSymbol> sub_symbol_list;
    Vector<String> tmp_symbol_list;
    Vector<XMLToken> grammar;
    String grammar_name;
    
    // process the grammar string
    //
    parseXMLGrammar(grammars(i), tmp_symbol_list, grammar, grammar_name);
    
    // convert the symbol list of Strings to
    // the sub symbol list of SearchSymbols
    //
    for (long i = 0; i < tmp_symbol_list.length(); i++) {
      SearchSymbol tmp;
      tmp.assign(tmp_symbol_list(i));
      sub_symbol_list.concat(tmp);
    }
    
    // check if any symbol in sub_symbol_list already exists in symbol_list
    //
    boolean same = false;
    for(long i=0; i<sub_symbol_list.length(); i++) {
      for(long j=0; j<symbol_list.length(); j++) {
	if(sub_symbol_list(i).eq(symbol_list(j))) {
	  same = true;
	  break;
	}
      }
      if (!same) {

	// add the sub symbol list
	//
	symbol_list.concat(sub_symbol_list(i));
      }
      same = false;
    }	
    
    // add the grammar name to the grammar name list if the grammar
    // is not empty
    //
    if (!grammar_name.eq(L"")) {
      grammar_name_list.concat(grammar_name);
      grammar_list.concat(grammar);
    }
    
  } // end of for loop 

  long num_grammars= grammar_list.length();
  
  // add the symbol list to the given search level 
  //
  hg_d(level_a).setSymbolTable(symbol_list);

  // call private method to re-orgnize the vector graph_list
  // to match the order of the symbols in the upper search level
  //
  if(level_a > 0) {
    
    // get the symbol table of the upper level
    //
    Vector<SearchSymbol>& symbol_table = *symbol_table_a;
    Vector<SearchSymbol> graph_symbols;
    
    // do we have context mapping ?
    //
    if (use_context_a) {
      
      // set the symbol table
      //
      for (long j = 0; j < num_grammars; j++) {
	String symbol;
	Ulong index;
	index.assign(j);
	symbol.assign(CONTEXT_LABEL_PREFIX);
	symbol.concat(index);
	graph_symbols.concat(symbol);
      }
    }
    
    // test the symbol_table
    //
    if (symbol_table_a == NULL && !use_context_a) {
      return Error::handle(name(), L"no symbol table of upper level",
			   ERR_CTXT_NO_SYM_TAB,
			   __FILE__, __LINE__);
    }
    
    // align the graphs in the graph list
    //
    if (use_context_a) {
      alignGraphs(grammar_list, graph_symbols, grammar_name_list);
    }
    else {
      alignGraphs(grammar_list, symbol_table, grammar_name_list);
    }
    
  }

  // add grammars to xml model
  //
  grammars_d.concat(grammar_list);
  
  
  // indicate success
  //
  return true;
}

// method: readSymbolType
//
// arguments:
//  Sof& sof: (input) sof file object
//  long level: (input) language model level
//  const String& name: (input) parameter name
//  Vector<SearchSymbol> symbol_list: (output) list of symbols
//
// return: a boolean value indicating status
//
// this method read one rule from the sof file
//
boolean LanguageModelXML::readSymbolType(Sof& sof_a, long level_a,
					 const String& name_a,
					 Vector<SearchSymbol>& symbol_list_a,
					 SofParser& parser_a) {
    // to read the string from the sof file
  //
  SofParser parser;

  // to store the grammar
  //
  String grammar;

  // name of the parameter to find
  //
  String param_name(name_a);
    
  if (level_a != DEF_LEVEL) {
    param_name.concat(PARAM_UNDERSCORE);
    param_name.concat((Long)level_a);
  }

  // read the grammar
  //
  if (parser_a.isPresent(sof_a, param_name)) {
    if (!grammar.readData(sof_a, param_name, parser_a.getEntry(sof_a,
							       param_name))) {
      return Error::handle(name(), L"readData",
			   ERR_READ_SYM_TYPE, __FILE__, __LINE__,
			   Error::WARNING);
    }

    // declare a lmxml object to read the grammar
    // a separate object is used here purposely. for debugging purposes
    // the symbol types are less useful than the main grammar, so the main
    // grammar is left intact in this object, and the symbol grammars
    // are stored in new objects 
    //
    LanguageModelXML lmxml;
    lmxml.setDebug(debug_level_d);

    Vector<String> temp_string_vector;
    Vector<XMLToken> token_vector;
    String grammar_name;
    
    // read a single grammar from that location
    //
    lmxml.parseXMLGrammar(grammar, temp_string_vector, token_vector, grammar_name);
    
    symbol_list_a.clear();
    
    // convert strings to search symbols.
    //
    for (int i = 0; i < temp_string_vector.length(); i++) {
      symbol_list_a.concat((SearchSymbol)temp_string_vector(i));
    }       
  }
  
  // indicate success
  //
  return true;
}

// method: alignGraphs
//
// arguments:
//    Vector< Digraph<String> >& graph_list: (output) vector of graphs
//    Vector<SearchSymbol>& symbol_table: (input) symbol table
//    Vector<String>& graph_name_list: (input) vector of grammar names
//
// return: a boolean value indicating status
//
// this method change the order of the graphs in given vector to
// match the order of search symbols in given symbol table
//
boolean LanguageModelXML::alignGraphs(Vector< Vector<XMLToken> >& grammar_list_a,
				      Vector<SearchSymbol>& symbol_table_a,
				      Vector<String>& grammar_name_list_a) {

  // make sure the three input vectors have the same length
  //
  if(grammar_list_a.length() != symbol_table_a.length() ||
     grammar_list_a.length() != grammar_name_list_a.length() ||
     grammar_name_list_a.length() != symbol_table_a.length()) {

    symbol_table_a.debug(L"symbol_table");
    grammar_name_list_a.debug(L"graph_name");
    
    return Error::handle(name(), L"alignGraphs", ERR_CTXT_INV_LENGTHS,
			 __FILE__, __LINE__);
  }

  // declare a temp vector of grammars
  //
  Vector< Vector<XMLToken> > tmp_grammar_list;

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

    // get symbol
    //
    String symbol(symbol_table_a(i));
    
    // ddd replace with vector contains?
    //
    for(long j=0; j<grammar_name_list_a.length(); j++) {

      // search for the matching grammar and add it to the temp vector
      //
      if(symbol.eq(grammar_name_list_a(j))) {
	tmp_grammar_list.concat(grammar_list_a(j));
	break;
      }
    } // end: for loop with j
  } // end: for loop with i
  
  // make sure the grammar_list_a and the tmp_grammar_list have the same length
  //
  if(grammar_list_a.length() != tmp_grammar_list.length()) {
    
    return Error::handle(name(), L"alignGraphs",
			 ERR_CTXT_INV_GRAPH_ALIGN, __FILE__, __LINE__);
  }
  
  // update the graph_list_a
  //
  grammar_list_a.assign(tmp_grammar_list);

  // exit gracefully
  //
  return true;
}

// method: readContextMapping
//
// arguments:
//   Sof& sof: (input) an Sof object initialized with a LanguageModel
//   const String& name: (input) the name of the context mapping parameter
//     that will be used to find the contexts in the sof file
//   long level: (input) the level of the hierarchical digraph currently
//     being processed.
//   SofParser& parser: (input) a SofParser with which to parse the file
//
// return: a boolean value indicating status
//
// This method reads the context mapping from a language model file and
// initializes the contexts of the HierarchicalDigraph
//
// Note - This method assumes that all of the non-context grammars within
// this level HAVE BEEN READ prior to the calling of this method.
//
boolean LanguageModelXML::readContextMapping(Sof& sof_a, const String& name_a,
					     long level_a, SofParser& parser_a) {

  // The HierarchicalDigraph contains two datastructures for storing contexts:
  // 1. A so-called context-map which is a Vector<ContextMap>, each ContextMap
  //    of which contains the SearchSymbol representations of the symbols
  //    belonging to a particular context, and the index to which this
  //    particular context belongs.
  // 2. A context-hash which is a HashTable<Ulong, Context>, which contains
  //    the index of a context, and a Context which contains a
  //    CircularDelayLine
  //    of the indices belonging to the SearchSymbols which represent the
  //    symbols belonging to a particular context.
  //
  //
  // Conceptually, the organization of this data could be viewed like this:
  //
  //                          Context Index
  //                            |      |
  //             (hg_d.context_map)    (hg_d.context_hash)  
  //                      |                   |
  //          Vector<SearchSymbol>          Context
  //                  'a'          -->      i = hg_d.getSymbolIndex('a')
  //                                        |
  //                  'b'          -->      j = hg_d.getSymbolIndex('b')
  //                                        |
  //                  'c'          -->      k = hg_d.getSymbolIndex('c')
  //
  // The index of the context can be used to link a symbol from that Context
  // with the index of that symbol within the HierarchicalDigraph. 

  // declare variables to store the raw input contexts, the
  // context maps generated from them, and a LMXML object
  // with which to parser the raw contexts.
  //
  Vector<String> input_context_strings;
  Vector<ContextMap> context_maps_vector;
  LanguageModelXML grammar_parser;
  
  // set up the parameter name to match the current level
  //
  String param_name(name_a);
  if (level_a != DEF_LEVEL) {
    param_name.concat(PARAM_UNDERSCORE);
    param_name.concat((Long)level_a);
  }

  // read the all the contextmaps as vector of string from the given
  // level-index
  //
  if (parser_a.isPresent(sof_a, param_name)) {
    if (!input_context_strings.readData(sof_a, param_name,
			       parser_a.getEntry(sof_a,
						 param_name),
			       false, false)) {
      return Error::handle(name(), L"readContextMapping", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }
  }

  // when there is NO context mapping table at this level
  //
  else {
    
    // symbols will be mapped to the model with the same index at lower level
    //
    Context dummy(1);

    // retrieve the contexthash. note that this is not a copy, this is
    // a reference, and modificatiosn to this context_hash will modify
    // the corresponding context_hash in the hierarchical digraph.
    //
    HashTable<Context, Ulong>& context_hash = hg_d(level_a).getContextHash();

    // for each symbol in the level, insert a dummy context with the index
    // of that symbol
    //
    for (Ulong i = 0;
	 i < (ulong)(hg_d(level_a).getSymbolTable()).length(); i++) {

      dummy.assignAndAdvance(i);
      context_hash.insert(dummy, &i);
    }

    if (debug_level_d >= Integral::ALL) {
      context_hash.debug(L"dummy context hash table:");
    }

    return true;
  } // end if no context mapping

  // make sure the symbol table has been read
  //
  long num_symbols = (hg_d(level_a).getSymbolTable()).length();
  if (num_symbols < 1) {
    return Error::handle(name(), L"readContextMapping", ERR_CTXT_NO_SYM_TAB,
			 __FILE__, __LINE__);
  }  
  
  // set the length of the context_maps_vector to the number of contexts
  // read
  //
  context_maps_vector.setLength(input_context_strings.length());
  
  // loop over all the input context strings, and parse them as XML
  // grammars. These context "grammars" each consist of a single rule
  // whose name contains all of the symbols within the context in
  // symbol1-symbol2-symbol3 fashion, and whose sole symbol is
  // G_# indicating Graph #, where # is the index of this context.
  //
  for(long i=0; i < input_context_strings.length(); i++) {

    // declare a vector to store a single context's symbols
    //
    Vector<SearchSymbol> context_symbols;

    // declare a vector to store a context symbol table
    //
    Vector<String> temp_symbol_table;

    // declare a vector to store grammar
    //
    Vector<XMLToken> temp_token_vector;
    
    // declare a String to store a single symbol as it is tokenized
    // from the grammar name
    //
    String symbol;

    // declare a string to store the grammar's name (which will be the
    // name of the only rule that is present in the grammar)
    //
    String grammar_name;
    
    // declare a string to store the index of the context, which will
    // be retrieved from the only symbol in the only rule
    //
    String context_index_string;

    // declare a variable to store the position within strings being
    // tokenized
    //
    long pos = 0;
    
    // declare a ulong to store the index of the context
    //
    ulong context_index_ulong;
    
    // parse the grammar to obtain the grammar name which contains
    // the context symbols, and the symbol table which will contain
    // the single symbol indicating the graph number
    //
    grammar_parser.parseXMLGrammar(input_context_strings(i), temp_symbol_table, temp_token_vector, grammar_name);

    // retrieve all the symbols from the grammar name
    //
    pos = 0;
    while(grammar_name.tokenize(symbol, pos, CONTEXT_SYMBOL_DELIM)) {

      // store all of the symbols tokenized
      //
      context_symbols.concat(symbol);
      
    } // end looping over embedded symbols

    // save the vector of symbols into the appropriate ContextMap within
    // the vector of context maps
    //
    if(!context_maps_vector(i).setContext(context_symbols)) {
      return Error::handle(name(), L"readContextMapping", ERR_CTXT_SCV,
			   __FILE__, __LINE__, Error::WARNING);    
    }

    // retrieve the only symbol from the only rule in the grammar
    //
    if(temp_symbol_table.length() != 1) {
      return Error::handle(name(), L"readContextMapping", ERR_CTXT_INV_SYM_TAB,
			   __FILE__, __LINE__, Error::WARNING);    
    }
    
    // retrieve the context index from the single symbol read from the
    // grammar. note that context_index_string will contain the last
    // token following a "G_" within the symbol. So, if symbol = "G_9",
    // context_index_string will = "9".
    //
    pos = 0;
    symbol.tokenize(context_index_string, pos, CONTEXT_LABEL_PREFIX);

    // set the context index of the current context map
    //
    if(!context_index_string.get(context_index_ulong)) {
      String err_str(L"readContextMapping: ");
      err_str.concat(context_index_string);
      return Error::handle(name(),err_str,
			   ERR_CTXT_INV_TOKEN_INDEX,
			   __FILE__, __LINE__, Error::WARNING);    
    }
    context_maps_vector(i).setContextIndex(context_index_ulong);
    
  } // end looping over input contexts

  // store the vector of context maps in the HierarchicalDigraph
  //
  if (!hg_d(level_a).setContextMap(context_maps_vector)) {
    return Error::handle(name(), L"readContextMapping", ERR_CTXT_ESHG,
			 __FILE__, __LINE__, Error::WARNING);    
  }

  // when there is a context mapping table at this level
  //
  if (context_maps_vector.length() > 0) {
    
    // add start and terminal search symbol to the symbol table
    //
    Vector<SearchSymbol>& symbol_table = hg_d(level_a).getSymbolTable();
    symbol_table.concat(SearchSymbol::NO_LEFT_CONTEXT);
    symbol_table.concat(SearchSymbol::NO_RIGHT_CONTEXT);
    
    // set up start and terminal search node for each subgraph
    //
    Vector<DiGraph<SearchNode > >& sub_graphs = hg_d(level_a).getSubGraphs();
    for (long i = 0; i < sub_graphs.length(); i++) {
      
      // start vertex
      //
      SearchNode* snode_p = new SearchNode();
      snode_p->setSearchLevel(&hg_d(level_a));
      snode_p->setSymbol(SearchSymbol::NO_LEFT_CONTEXT);
      sub_graphs(i).getStart()->setItem(snode_p);
      
      // terminal vertex
      //
      snode_p = new SearchNode();
      snode_p->setSearchLevel(&hg_d(level_a));
      snode_p->setSymbol(SearchSymbol::NO_RIGHT_CONTEXT);
      sub_graphs(i).getTerm()->setItem(snode_p);
      
    } // end looping over subgraphs
    
    // insert all context pairs into the context mapping hash table
    //
    long total_context_length = context_maps_vector(0).getContext().length();
    Context context(total_context_length);
    
    // loop over all context pairs
    //
    for (long i = 0; i < context_maps_vector.length(); i++) {
      
      // loop over the symbols in the context
      //
      for (long j = 0; j < total_context_length; j++) {
	
	SearchSymbol ss(context_maps_vector(i).getContext()(j));
	long symbol_id = hg_d(level_a).getSymbolIndex(ss);
	
	// check whether the symbol is valid
	//
	if (symbol_id == Integral::NO_POS) {
	  ss.debug(L"symbol");
	  return Error::handle(name(), L"readContextMapping", ERR_CTXT_INV_SYM,
			       __FILE__, __LINE__);
	}
	else {
	  context.assignAndAdvance(symbol_id);
	}
      } // end looping over symbols in context
      
      // insert a context specification into the hash table
      //
      Ulong index = context_maps_vector(i).getContextIndex();
      
      // check if the context is already in the table
      //
      HashTable<Context, Ulong>& context_hash = hg_d(level_a).getContextHash();
      Ulong* existing_index = context_hash.get(context);
      
      // if this context is not in the table yet, insert it
      //
      if (existing_index == NULL) {
	context_hash.insert(context, &index);
      }
      
      // if the context is already in the table, then check whether
      // the index of the model is the same as the one from table
      //
      else {
	
	// if indices are different, explain conflict and return error
	//
	if (!existing_index->eq(index)) {
	  
	  context_maps_vector(i).debug(L"Context:");
	  String out;
	  out.concat(L" is already in context mapping table with the index: ");
	  out.concat(*existing_index);
	  out.concat(L"\n while new index is: ");
	  out.concat(index);
	  Console::put(out);
	  return Error::handle(name(), L"readContextMapping",
			       ERR_CTXT_EXISTS_DIF_INDEX,
			       __FILE__, __LINE__);
	}
	
	// otherwise indices are not conflicting, just print a warning
	//
	else {
	  context_maps_vector(i).debug(L"Warning: This context: ");
	  String out;
	  out.concat(L" is already in context mapping table with the index: ");
	  out.concat(*existing_index);
	  out.concat(SysChar::NEWLINE);
	  Console::put(out);
	  Error::handle(name(), L"readContextMapping",
			ERR_CTXT_EXISTS_SAME_INDEX,
			__FILE__, __LINE__, Error::WARNING);
	}
	
      }
      
      // output the debugging information
      //
      if (debug_level_d >= Integral::ALL) {
	
	// retrieve the context hash
	//
	HashTable<Context, Ulong>& context_hash =
	  hg_d(level_a).getContextHash();
	
	// output the context hash information
	//
	context_hash.debug(L"context hash table:");
	
      } // end debugging info
    } // end looping over all contexts
  } // end handling context mapping for level
  
  // indicate success
  //
  return true;
}
