// file: $isip/class/search/StackSearch/ssrch_09.cc
// version: $Id: ssrch_09.cc 9144 2003-05-16 21:59:27Z jelinek $
//

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

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

  // make sure that we have at least one level defined
  //
  if (getNumLevels() < 1) {
    return Error::handle(name(), 
			 L"initializeLinearDecoder - read models first",
			 StackSearch::ERR_LEVELS_NOT_LOADED,
			 __FILE__, __LINE__);
  }
  
  // set the current frame
  //
  current_frame_d = 0; 

  // clear the valid_hyps_d list
  //
  clearTraceStorage();
  
  // clear the trace lists of all the search nodes
  //
  clearSearchNodesTraceLists();

  // clear the history pool
  //
  if (history_pool_d.length() > 0) {
    history_pool_d.clear(Integral::RETAIN);
  }
  
  // clear the context pool
  //
  if (context_pool_d.length() > 0) {
    context_pool_d.clear(Integral::RETAIN);
  }
  
  // generate all initial right context traces
  //
  generateInitialTraces();
  
  // exit gracefully
  //
  return true;
}

// method: generateInitialTraces
//
// arguments: none
//
// return: logical error status
//
// generate one trace for each right context at the top level
//
boolean StackSearch::generateInitialTraces() {

  // get the position of the central item in the context
  //
  long central_pos = getSearchLevel(0).getRightContext() + 1;

  // get the total context length
  //
  long total_context_length = getSearchLevel(0).getLeftContext()
                                                          + central_pos;
  
  // initialize the left context
  //
  Context left_context(total_context_length, central_pos);
  for (long i = 0; i < central_pos; i++) {
    left_context.assignAndAdvance(
                   (ulong)getSearchLevel(0).getSubGraph(0).getStart()
		   );
  }

  // generate all possible right contexts by extending left context to
  // the depth of the right context
  //
  Context* symbol = context_pool_d.get(left_context);
  DoubleLinkedList<Context> right_context_list(DstrBase::USER);
  generateRightContexts(right_context_list, symbol,
			getSearchLevel(0).getRightContext());
  
  // generate trace for each of above generated right contexts
  //
  for (boolean more_items = right_context_list.gotoFirst();
       more_items; more_items = right_context_list.gotoNext()) {
    
    // seed the top trace list with the start node of the search graph
    //
    Trace* tmp_trace = new Trace();
    num_traces_gen_d(current_frame_d)++;
    tmp_trace->setFrame((ulong)current_frame_d);
    tmp_trace->setBackPointer((Trace*)NULL);
    tmp_trace->setSymbol(right_context_list.getCurr());
    
    // has the history been previously generated? if so reuse the history,
    // if not generate a new history and add it to the history pool
    //
    tmp_trace->setHistory(history_pool_d.initAndAllocate());
    
    // add the start trace to the list
    //
    trace_lists_d(0).insertLast(tmp_trace);
    
    // output the debugging information
    //
    if (debug_level_d >= Integral::ALL) {
      String out;
      out.concat(L"initial trace: ");
      out.concat(tmp_trace);
      Console::put(out);
      printNewPath(tmp_trace, (Trace*)NULL);
    }
  }
    
  // clear the memory
  //
  right_context_list.clear();

  // exit gracefully
  //
  return true;
}

// method: initSizes
//
// arguments:
//  long total_num_frames: (input) total number of frames to decode
//
// return: logical error status
//
// set the features, initialize stacks and maximal stack scores
//
boolean StackSearch::initSizes(long total_num_frames_a) {

  if (total_num_frames_a > MAX_NUM_FRAMES) {
    return Error::handle(name(), L"initSizes - too long file to decode",
			 Error::ARG, __FILE__, __LINE__);
  }

  // allocate and initialize maximal scores for each stack
  //
  for (long i = 0; i <= total_num_frames_a; i++) {
    max_stack_scores_d(i) = Trace::INACTIVE_SCORE;
  }

  // check whether the search levels are loaded
  //
  if (getNumLevels() < 1) {
    return Error::handle(name(), 
			 L"setFeatures - read models first",
			 StackSearch::ERR_LEVELS_NOT_LOADED,
			 __FILE__, __LINE__);
  }
  
  // check whether the statistical models are loaded
  //
  long num_stat_models =
    getSearchLevel(getNumLevels() - 1).getStatisticalModels().length();
  if (num_stat_models == 0) {
    return Error::handle(name(),
			 L"setFeatures - read models first",
			 StackSearch::ERR_LEVELS_NOT_LOADED,
			 __FILE__, __LINE__);
  }
  stat_model_scores_d.setDimensions(MAX_NUM_FRAMES, num_stat_models);
  
  // initialize maximal trace scores at each frame for all levels and
  // statistical model scores for each frame and each statistical
  // model
  //
  for (long i = 0; i < total_num_frames_a; i++) {
    for (long j = 0; j < getNumLevels(); j++) {
      max_frame_scores_d.setValue(i, j, Trace::INACTIVE_SCORE);
    }
    for (long j = 0; j < num_stat_models; j++) {
      stat_model_scores_d.setValue(i, j, Trace::INACTIVE_SCORE);
    }
  }

  // initialize the score of the best valid hypothesis to very small
  // value
  //
  best_valid_score_d = Trace::INACTIVE_SCORE;

  // set up the mapping of states to statistical model indices (if
  // efficient, this mapping should be set up in the search level)
  //
  // check if it is not done yet (while decoding previous utterances)
  //
  if (stat_model_mapping_d.length() == 0) {

    // get the number of states from state level symbol table
    //
    SearchLevel& state_level = getSearchLevel(getNumLevels() - 1);
    Vector<SearchSymbol>& state_symbol_table = state_level.getSymbolTable();
    
    HashTable<SearchSymbol, Long>& symbol_hashtable =
      state_level.getSymbolHashTable();

    // check if hash table have been loaded
    //
    if (symbol_hashtable.getNumItems() == 0) {
      return Error::handle(name(),
			   L"initSizes - load stat models hashtable first",
			   Error::ARG, __FILE__, __LINE__);
    }

    long num_states = state_symbol_table.length();
    stat_model_mapping_d.setLength(num_states);
    for (long i = 0; i < num_states; i++) {
      SearchSymbol& state_symbol = state_symbol_table(i);
      Long* p_stat_model_id = symbol_hashtable.get(state_symbol);
      stat_model_mapping_d(i) = *p_stat_model_id;
    }
  }

  // initialize evaluation and trace statistics
  //
  for (long i = 0; i <= total_num_frames_a; i++) {
    num_traces_gen_d(i) = 0;
    num_traces_vit_prun_d(i) = 0;
    num_traces_beam_prun_d(i) = 0;
    num_eval_d(i) = 0;
    num_avoided_eval_d(i) = 0;
  }
  
  // exit gracefully
  //
  return true;
}

// method: determineEvaluationMode
//
// arguments: none
//
// return: logical error status
//
// set evaluation mode to SEGMENT or FRAME depending on the stat models loaded
//
boolean StackSearch::determineEvaluationMode() {

  SearchLevel& lowest_level = getSearchLevel(getNumLevels() - 1);

  if (lowest_level.getStatisticalModels()(0).getType() == 
      StatisticalModel::SUPPORT_VECTOR_MODEL) {

    // for SVM, we evaluate segmental (average) features
    //
    evaluation_mode_d = SEGMENT_EVAL;
  }
  else {
    evaluation_mode_d = FRAME_EVAL;
  }

  // exit gracefully
  //
  return true;
}

// method: normalizeFeatures
//
// arguments: none
//
// return: logical error status
//
// this method is used to normalize mfcc features to range <-1, 1>
//
boolean StackSearch::normalizeFeatures() {

  VectorFloat max(features_d(0));
  VectorFloat min(features_d(0));
  
  if (max_min_filename_d.length() != 0) {
    // code to load max and min vector from file
    //
    File mmf;
    mmf.open(max_min_filename_d);

    String line;
    mmf.get(line);

    mmf.close();

    VectorFloat v(line);
    v.debug(L"v");
    for (long i = 0; i < features_d(0).length(); i++) {
      max(i) = v(2*i);
      min(i) = v(2*i + 1);
    }
    if (debug_level_d >= Integral::BRIEF) {
      Console::put(L"Using this max-min file for feature normalization:");
      Console::put(max_min_filename_d);
      max.debug(L"max");
      min.debug(L"min");
    }
  }
  // find a maximum from the featues in this utternace
  //
  else {
    Console::put(L" Warning: min-max file not specified, doing utterance based normalization");
    for (long i = 1; i < features_d.length(); i++) {
      for (long j = 0; j < features_d(0).length(); j++) {
	if (features_d(i)(j) > max(j)) {
	  max(j) = features_d(i)(j);
	}
	else if (features_d(i)(j) < min(j)) {
	  min(j) = features_d(i)(j);
	}
      }
    }
  }

  // normalize featues to be in the range <-1, 1>
  //
  for (long i = 0; i < features_d.length(); i++) {
    for (long j = 0; j < features_d(0).length(); j++) {
      features_d(i)(j) = 2*(features_d(i)(j) - min(j))/(max(j) - min(j)) - 1;
    }
  }

  // exit gracefully
  //
  return true;
}
