// file: $isip/class/search/Context/cont_05.cc
// version: $Id: cont_05.cc 9029 2003-02-15 17:08:33Z alphonso $
//

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

// method: assign
//
// arguments:
//  const Context& copy_context: (input) context to copy
//
// return: logical error status
//
// assign context from the copy
//
boolean Context::assign(const Context& copy_context_a) {

  // clear the contents
  //
  clear();
  
  // copy all internal data
  //
  CircularDelayLine<Ulong>::assign(copy_context_a);
  central_d = copy_context_a.central_d;
  ext_length_d = copy_context_a.ext_length_d;
  
  // exit gracefully
  //
  return true;
}

// method: eq
//
// arguments:
//  const Context& context: (input) Context object to compare
//
// return: a boolean value indicating status
//
// this method checks if the input object is the same as the current one
//
boolean Context::eq(const Context& context_a) const {

  // get the context length
  //
  long len = length();
  
  // check to see if the context lengths are the same
  //
  if (len == context_a.length()) {
    for (long i = 0; i < len; i++) {
      if ((*this)(i) != context_a(i)) {
	return false;
      }
    }    
  }
  else {
    return false;
  }
  
  // if we reached this far then the items must be identical
  //
  return true;  
}

// method: hash
//
// arguments:
//  long capacity: (input) upper bound on the hash value
//
// return: an index to the hash table of model indices
//
// this is the hash method
// hash key is an array of symbol indices, hashed item is an index of
// lower level model for this context symbol sequence
// Note: if the context contains array of pointers to graph vertices,
// we need to convert them to symbol indices before hashing (using
// the convert() method)
//
long Context::hash(long capacity_a) const {

  // declare local values
  //
  ulong hash_index = (ulong)0;

  for (long i = v_d.length(); i > 0; i--) {
    ulong index = (ulong)(*this)(-i);
    hash_index = (hash_index << 5) ^ (hash_index >> 27) ^ index;
  }
  hash_index = (ulong)(hash_index % (capacity_a - 1));

  // return the hash index
  //
  return hash_index;
}

// method: print
//
// arguments:
//  String& output: (output) string of context symbols (e.g. "a-b+c")
//
// return: logical error status
//
// this method prints the comment and context symbols left context
// symbols are followed by "-" right context symbols follow "+"
//
boolean Context::print(String& output_a) {

  // declare local variables
  //
  SearchSymbol symbol;

  // loop over all context vertices
  //
  for (long i = length(); i > 0 ; i--) {

    // get the pointer to the graph vertex
    //
    GVSnode* vertex = (GVSnode*)(ulong)(*this)(-i);
    
    // get the symbol corresponding to the graph vertex
    //
    if (vertex == (GVSnode*)NULL) {
      output_a.concat(L"null vertex!");
    }
    else if (vertex->isStart()) {
      output_a.concat(SearchSymbol::NO_LEFT_CONTEXT);
    }
    else if (vertex->isTerm()) {
      output_a.concat(SearchSymbol::NO_RIGHT_CONTEXT);
    }
    else {
      vertex->getItem()->getSymbol(symbol);
      output_a.concat(symbol);
    }
    
    // distinguish the left, right and extended context symbols
    //
    if (i > (central_d + ext_length_d)) {
      output_a.concat(L"-");
    }
    else if (i > (1 + ext_length_d)) {
      output_a.concat(L"+");
    }
    else if (i > 1) {
     output_a.concat(L"|");
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: print
//
// arguments: none
//
// return: logical error status
//
// this method prints the comment and context symbols left context
// symbols are followed by "-" right context symbols follow "+"
//
boolean Context::print() {

  // declare local variables
  //
  String output;  
  SearchSymbol symbol;

  // appeand the initial comment
  //
  output.concat(L"context: ");
  
  // loop over all context vertices
  //
  for (long i = length(); i > 0 ; i--) {

    // get the pointer to the graph vertex
    //
    GVSnode* vertex = (GVSnode*)(ulong)(*this)(-i);
    
    // get the symbol corresponding to the graph vertex
    //
    if (vertex == (GVSnode*)NULL) {
      output.concat(L"null vertex!");
    }
    else if (vertex->isStart()) {
      output.concat(SearchSymbol::NO_LEFT_CONTEXT);
    }
    else if (vertex->isTerm()) {
      output.concat(SearchSymbol::NO_RIGHT_CONTEXT);
    }
    else {
      vertex->getItem()->getSymbol(symbol);
      output.concat(symbol);
    }
    
    // distinguish the left, right and extended context symbols
    //
    if (i > (central_d + ext_length_d)) {
      output.concat(L"-");
    }
    else if (i > (1 + ext_length_d)) {
      output.concat(L"+");
    }
    else if (i > 1) {
     output.concat(L"|");
    }
  }

  // print the context
  //
  Console::put(output);
  
  // exit gracefully
  //
  return true;
}

// method: convert
//
// arguments: none
//
// return: a boolean value indicating status
//
// this method converts pointers to graph vertices in this context to
// search symbol indices (not reversible)
//
boolean Context::convert() {

  // get the indices of all search symbols from the vertices
  //
  for (long i = 0; i < length(); i++) {
      
    GVSnode* vertex = (GVSnode*)(ulong)v_d(i);
      
    // check for the NULL vertex
    //
    if (vertex == (GVSnode*)NULL) {
      vertex = getCentralVertex()->getItem()->getSearchLevel()->
	getSubGraph((long)0).getTerm();
    }

    // get the symbol index of the sear node in vertex
    //
    long sym_id = vertex->getItem()->getSymbolId();
    v_d(i) = sym_id;
  }

  // output the debugging information
  //
  if (debug_level_d >= Integral::ALL) {

    String output(L"converted context: ");
    for (long i = length(); i > 0 ; i--) {
      output.concat((*this)(-i));
    }
    Console::put(output);
  }
  
  // exit gracefully
  //
  return true;
}

// method: convert
//
// arguments:
//   Context*& output_a: (output) symbol indices of the graph vertices
//   boolean extended_a: (input) flag that indicates and extended context
//
// return: a boolean value indicating status
//
// this method converts pointers to graph vertices in this context to
// search symbol indices (not reversible)
//
boolean Context::convert(Context*& output_a, boolean extended_a) {

  // determine the total context length
  //  
  long total_length = length();
  long context_length = total_length - ext_length_d;
  
  // initialize the output context
  //
  if (output_a != (Context*)NULL) {
    return Error::handle(name(), L"convert - output context is not null", Error::ARG, __FILE__, __LINE__);
  }
  output_a = new Context(context_length, central_d);
  
  // loop over all context symbols in current context, starting from
  // the oldest one
  //
  GVSnode* vertex = (GVSnode*)NULL;  
  for (long i = total_length; i > ext_length_d; i--) {
    
    // get the i-th previous context symbol
    //
    if (extended_a) {
      vertex = (GVSnode*)(ulong)(*this)(-i + ext_length_d);
    }
    else {
      vertex = (GVSnode*)(ulong)(*this)(-i);
    }
    
    // check for the NULL vertex
    //
    if (vertex == (GVSnode*)NULL) {
      vertex = getCentralVertex()->getItem()->getSearchLevel()->
	getSubGraph((long)0).getTerm();
    }

    // get the symbol index of the search node in vertex
    //
    ulong symbol_id = vertex->getItem()->getSymbolId();
    output_a->assignAndAdvance(symbol_id);
  }
  
  // exit gracefully
  //
  return true;
}

//
// arguments: none
//
// return: a boolean value indicating status
//
// this methods shifts the extended context and reduces the size by one
//
boolean Context::fold() {

  // declare local variables
  //
  Vector<Ulong> vec;  
  long total_length = length();
  
  // make sure the context has been extended
  //
  if (ext_length_d < 1) {
    return Error::handle(name(), L"fold", Error::ARG, __FILE__, __LINE__);
  }

  // rearrange items
  //
  vec.setLength(total_length - 1);
  for (long i = 1; i < total_length; i++) {
    vec(i - 1) = (*this)(i);
  }

  // reset the index and extension length
  //
  index_d = 0;  
  ext_length_d--;
  
  // assign the new folded context
  //
  v_d.assign(vec);
  
  // exit gracefully
  //
  return true;  
}

// method: extend
//
// arguments:
//  ulong pointer_to_vertex: (input) pointer to the vertex to be appended
//
// return: a boolean value indicating status
//
// this method appends a pointer to graph vertex to the context
//
boolean Context::extend(GVSnode* pointer_to_vertex_a) {

  // set the extension length, the extension length is zero in case we
  // have deep enough context
  //
  ext_length_d++;

  // if right context is extended deeper than right context length
  // then the buffer capacity have to be increased
  //
  if (ext_length_d > 0) {
    long new_length = length() + 1;

    v_d.setLength(new_length);

    // rearrange items
    //
    for (long i = new_length - 1; i > index_d; i--) {
      v_d(i) = v_d(i - 1);
    }
  }

  // append vertex
  //
  Ulong tmp = (ulong)pointer_to_vertex_a;
  assignAndAdvance(tmp);

  // exit gracefully
  //
  return true;
}
