// file: $isip/class/dstr/UGraph/UGraph.h
// version: $Id: UGraph.h 6197 2001-01-17 17:47:00Z alphonso $
//

// make sure definitions are only made once
//
#ifndef ISIP_U_GRAPH
#define ISIP_U_GRAPH

// isip include files
//
#ifndef ISIP_DI_GRAPH
#include <DiGraph.h>
#endif

#ifndef ISIP_DOUBLE_LINKED_LIST
#include <DoubleLinkedList.h>
#endif

#ifndef ISIP_BOOLEAN
#include <Boolean.h>
#endif

#ifndef ISIP_COLOR_HASH
#include <ColorHash.h>
#endif

#ifndef ISIP_GRAPH_VERTEX
#include <GraphVertex.h>
#endif

// forward class definitions:
//  we must define the GraphVertex class here first because the header files
//  might be short-circuited by the ifndef.
//
template<class TObject> class ColorHash;
template<class TObject> class GraphVertex;

// UGraph: a generic two-way directed-graph template class. it is
// implemented with something close to an adjacency list, with data
// held on the vertices. each vertex holds both a TObject* and a list
// of all emanating arcs. a graph can be in either USER or SYSTEM
// allocated mode, for a USER-allocated list the TObject data is never
// copied.
//
template<class TObject>
class UGraph : public DiGraph<TObject> {

  //---------------------------------------------------------------------------
  //
  // public constants
  //
  //---------------------------------------------------------------------------
public:
  
  // define the class name
  //
  static const String CLASS_NAME;
  
  //----------------------------------------
  //
  // i/o related constants
  //
  //----------------------------------------  
  
  //----------------------------------------
  //
  // dummy objects for start and end vertices
  //
  //----------------------------------------  

  //----------------------------------------
  //
  // default values and arguments
  //
  //----------------------------------------

  // default values
  //

  // default arguments to methods
  //
  
  //----------------------------------------
  //
  // error codes
  //
  //----------------------------------------  
  
  //---------------------------------------------------------------------------
  //
  // protected data
  //
  //---------------------------------------------------------------------------
protected:

  //---------------------------------------------------------------------------
  //
  // required public methods
  //
  //---------------------------------------------------------------------------
public:

  // static methods
  //  diagnose method is moved outside the class header file and
  //  defined in the DiGraphDiagnose.h in order to avoid issues
  //  related to preprocessing of the diagnose code.
  //
  static const String& name();

  // destructor
  //
  ~UGraph() {}

  // default constructor
  //
  UGraph(ALLOCATION alloc = DEF_ALLOCATION) : DiGraph<TObject> (alloc) {}

  // copy constructor
  //
  UGraph(const UGraph<TObject>& copy_graph) : DiGraph<TObject> (copy_graph) {}

  // assign methods:
  //  having the same vertex appear in two different graphs is not allowed.
  //  thus, memory is created for copied vertices even if the graph is in
  //  user-allocating mode!
  //
  boolean assign(const UGraph<TObject>& copy_graph);

  // method: operator=
  //
  UGraph<TObject>& operator=(const UGraph<TObject>& arg) {
    assign(arg);
    return *this;
  }

  // equality method
  //
  boolean eq(const UGraph<TObject>& compare_graph) const;
  
  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  graph manipulation methods
  //
  //---------------------------------------------------------------------------

  // method: insertArc
  //
  boolean insertArc(long start_index, long end_index,
		    boolean is_epsilon = GraphArc<TObject>::DEF_EPSILON,
		    float weight = GraphArc<TObject>::DEF_WEIGHT) {
    return insertArc(getPosition(start_index), getPosition(end_vertex),
		     is_epsilon, weight);
  }

  // other arc insertion methods
  //
  boolean insertArc(GraphVertex<TObject>* start_vertex,
		    GraphVertex<TObject>* end_vertex,
		    boolean is_epsilon = GraphArc<TObject>::DEF_EPSILON,
		    float weight = GraphArc<TObject>::DEF_WEIGHT);
  
  // method: removeArc
  //
  boolean removeArc(long start_index, long end_index) {
    return removeArc(getPosition(start_index), getPosition(end_vertex));
  }

  // other arc removal methods
  //  
  boolean removeArc(GraphVertex<TObject>* start_vertex,
		    GraphVertex<TObject>* end_vertex);

  //---------------------------------------------------------------------------
  //
  // private methods
  //
  //---------------------------------------------------------------------------
private:

};

//-----------------------------------------------------------------------------
//
// we define non-integral constants at the end of class definition for
// templates (for non-templates these are defined in the default constructor)
//      
//-----------------------------------------------------------------------------

// constants: required constants such as the class name
//
template <class TObject>
const String UGraph<TObject>::CLASS_NAME(L"UGraph");

// below are all the methods for the UGraph template class
//      

// ---------------------------------------------------------------------
//
// required static methods
//
//----------------------------------------------------------------------

// method: name
//
// arguments: none
//
// return: a static String& containing the class name
//
// this method returns the class name
//
template<class TObject>
const String& UGraph<TObject>::name() {

  // create the static name string for this class and return it
  //
  static String cname(CLASS_NAME);
  cname.clear(Integral::RESET);
  cname.concat(CLASS_NAME);
  cname.concat(L"<");
  cname.concat(TObject::name());
  cname.concat(L">");

  // return the name
  //
  return cname;
}

//-------------------------------------------------------------------------
//
//  required assign methods
//
//-------------------------------------------------------------------------

// method: assign
//
// arguments:
//  UGraph<TObject>& copy_graph: (input) the graph to copy
//
// return: a boolean value indicating status
//
// this is the assign method for the UGraph class. we can't just copy
// the data over since the arcs make connections by pointer (and we
// can't share vertices across two graphs), hence we must manually
// insert every vertex and every arc. new vertices are created, but if
// the graph is in USER mode the tobject is not copied.
//
template<class TObject>
boolean UGraph<TObject>::assign(const UGraph<TObject>& arg_a) {

  // copy the weighting flag.
  //
  is_weighted_d = arg_a.is_weighted_d;

  // copy the allocation flag
  //
  alloc_d = arg_a.alloc_d;
  
  // save the state of the input graph
  //
  long old_pos = const_cast<UGraph<TObject>& >(arg_a).getPosition();
  
  // we need to build a cross-referencing scheme to make sure we cover
  // all arcs and all nodes. the (+2) is for the start and terminal
  // vertices since they are NOT included in the list
  //
  long num_vertices = arg_a.length() + 2;
  
  GraphVertex<TObject>* copy_sym_ptrs[num_vertices];
  GraphVertex<TObject>* this_sym_ptrs[num_vertices];

  // set the start and terminal vertices
  //
  copy_sym_ptrs[0] = arg_a.getStart();
  copy_sym_ptrs[num_vertices - 1] = arg_a.getTerm();
  
  // loop through the list of vertices, and number them
  //
  const_cast<UGraph<TObject>& >(arg_a).gotoFirst();

  for (long i = 1; i < num_vertices - 1; i++) {

    // get the current node in the graph and assign it to the array
    //
    copy_sym_ptrs[i] = const_cast<GraphVertex<TObject>* >
      (arg_a.getCurr());

    // move to the next node in the graph
    //
    const_cast<UGraph<TObject>& >(arg_a).gotoNext();
  }

  // before we assign the input graph we need to clear the current graph
  //
  this->clear(Integral::RESET);
  
  // set the start and terminal vertices
  //
  this_sym_ptrs[0] = getStart();
  this_sym_ptrs[num_vertices - 1] = getTerm();  

  for (long i = 1; i < num_vertices - 1; i++) {

    // create a new graph vertex
    //
    this_sym_ptrs[i] = this->insertVertex(copy_sym_ptrs[i]->getItem());
  }

  // now build the arclists for each node
  //
  for (long i = 0; i < num_vertices; i++) {

    // get the vertex in question
    //
    GraphVertex<TObject>* tmp_copy_vert = copy_sym_ptrs[i];

    // loop over the copy graph's arclist
    //
    boolean arcs_remain = tmp_copy_vert->gotoFirst();

    while (arcs_remain) {

      // setup temporary variables
      //
      long dest_index = -1;
      GraphArc<TObject>* tmp_arc = tmp_copy_vert->getCurr();
      GraphVertex<TObject>* dest_vertex = tmp_arc->getVertex();

      // find the destination vertex index
      //
      for (long j = 0; j < num_vertices; j++) {
	if (dest_vertex == copy_sym_ptrs[j]) {
	  dest_index = j;
	  break;
	}
      }

      if (dest_index == -1) {
	return Error::handle(name(), L"assign", Error::ARG,
			     __FILE__, __LINE__);
      }

      // link the vertices in the new graph
      //
      DiGraph<TObject>::insertArc(this_sym_ptrs[i], this_sym_ptrs[dest_index],
				  tmp_arc->getEpsilon(), tmp_arc->getWeight());

      // goto the next arc
      //
      arcs_remain = tmp_copy_vert->gotoNext();
    }
  }

  // put the copy graph back in its original state
  //
  const_cast<UGraph<TObject>& >(arg_a).gotoPosition(old_pos);

  // exit gracefully
  //
  return true;
}

//-------------------------------------------------------------------------
//
//  required equality method
//
//-------------------------------------------------------------------------

// method: eq
//
// arguments:
//  const UGraph<TObject>& graph: (input) the graph to compare
//
// return: boolean value indicating test of equivalence
//
// this method compares two graph to see if they are equal. it can't
// just call list-equality since all vertex pointers will be
// different.
//
template<class TObject>
boolean UGraph<TObject>::eq(const UGraph<TObject>& graph_a) const {

  // create lists to extract the graph structures
  //
  SingleLinkedList<TObject> this_dlist;
  SingleLinkedList<Triple<Pair<Long, Long>, Float, Boolean> > this_alist;
  SingleLinkedList<TObject> input_dlist;
  SingleLinkedList<Triple<Pair<Long, Long>, Float, Boolean> > input_alist;  

  // extract the structure of the current list
  //
  const_cast<UGraph<TObject>* >(this)->get(this_dlist, this_alist);
  
  // extract the structure of the input list
  //  
  const_cast<UGraph<TObject>& >(graph_a).get(input_dlist, input_alist);

  // compare the lists for quality
  //
  if (!this_dlist.eq(input_dlist)) {
    return false;
  }
  if (!this_alist.eq(input_alist)) {
    return false;
  }
  
  // we have reached this far so the graphs have to be equal
  //  
  return true;
}

//---------------------------------------------------------------------------
//
// class-specific public methods:
//  insert/remove arc methods
//
//---------------------------------------------------------------------------

// method: insertArc
//
// arguments:
//  GraphVertex<TObject>* start_vertex: (input) the start vertex
//  GraphVertex<TObject>* end_vertex: (input) the ending vertex
//  boolean is_epsilon: (input) is the arc an epsilon transition
//  float weight: (input) the weight on the arc (if any)
//  
// return: a boolean value indicating status
//
// this method inserts arcs between two vertices in the graph provided
// that the two vertices are already present in the graph
//
template<class TObject>
boolean UGraph<TObject>::insertArc(GraphVertex<TObject>* start_vertex_a,
				  GraphVertex<TObject>* end_vertex_a,
				  boolean is_epsilon_a, float weight_a) {

  // declare local variables
  //
  boolean return_val1 = false;
  boolean return_val2 = false;
  
  // make sure neither vertex is NULL
  //
  if ((start_vertex_a == (GraphVertex<TObject>*)NULL) ||
      (end_vertex_a == (GraphVertex<TObject>*)NULL)) {
    return Error::handle(name(), L"insertArc", Error::NULL_ARG,
			 __FILE__, __LINE__);
  }
  
  // we don't allow connections between graphs so make sure the parent graph
  // for both vertices is either already this graph or is null
  //
  if (((start_vertex_a->getParentGraph() != (UGraph<TObject>*)NULL) &&
       (start_vertex_a->getParentGraph() != this)) ||
      ((end_vertex_a->getParentGraph() != (UGraph<TObject>*)NULL) &&
       (end_vertex_a->getParentGraph() != this))) {
    return Error::handle(name(), L"insertArc", ERR_MULPAR, __FILE__,
			 __LINE__);
  }
  
  // make sure that the start vertex and end vertex are already in the graph
  //
  if (!contains(start_vertex_a)) {
    if ((start_vertex_a != start_vertex_d) &&
	(start_vertex_a != term_vertex_d)) {
      return Error::handle(name(), L"insertArc", Error::ARG,
			   __FILE__, __LINE__);
    }
  }
  if (!contains(end_vertex_a)) {
    if ((end_vertex_a != start_vertex_d) &&
	(end_vertex_a != term_vertex_d)) {
      return Error::handle(name(), L"insertArc", Error::ARG,
			   __FILE__, __LINE__);
    }
  }
  
  // set the parent pointers regardless - this may be redundant but it is
  // probably no less efficient than putting an if statement around it
  //
  start_vertex_a->setParentGraph(this);
  end_vertex_a->setParentGraph(this);

  // make sure that we do not duplicate self arcs
  //
  if (start_vertex_a == end_vertex_a) {
    return_val2 = true;
  }
  
  // add an arc from the start to the end
  //
  return_val1 = start_vertex_a->insertArc(end_vertex_a, weight_a,
					  is_epsilon_a);

  // add an arc from the end to the start
  //
  if (start_vertex_a != end_vertex_a) {  
    return_val2 = end_vertex_a->insertArc(start_vertex_a, weight_a,
						  is_epsilon_a);
  }
  
  // exit gracefully
  //
  return (return_val1 & return_val2);
}

// method: removeArc
//
// arguments:
//  GraphVertex<TObject>* start_vertex: (input) the start vertex
//  GraphVertex<TObject>* end_vertex: (input) the ending vertex
//  
// return: a boolean value indicating status
//
// this method removes the arcs between two vertices from the graph structure
// provided that the two vertices are already present in the graph
//
template<class TObject>
boolean UGraph<TObject>::removeArc(GraphVertex<TObject>* start_vertex_a,
				    GraphVertex<TObject>* end_vertex_a) {
  
  // make sure neither vertex is NULL
  //
  if ((start_vertex_a == (GraphVertex<TObject>*)NULL) ||
      (end_vertex_a == (GraphVertex<TObject>*)NULL)) {
    return Error::handle(name(), L"removeArc", Error::NULL_ARG,
			 __FILE__, __LINE__);
  }

  // make sure that both the start and end vertices are in the graph
  //
  if (!contains(end_vertex_a)) {
    if (end_vertex_a != term_vertex_d) {
      return Error::handle(name(), L"removeArc", ERR,
			   __FILE__, __LINE__);
    }
  }
  if (!contains(start_vertex_a)) {
    if (start_vertex_a != start_vertex_d) {
      return Error::handle(name(), L"removeArc", ERR,
			   __FILE__, __LINE__);
    }
  }
  
  // remove the arc form the start to the end vertex
  //
  boolean return_val1 = start_vertex_a->removeArc(end_vertex_a);

  // remove the arc form the end to the start vertex
  //
  boolean return_val2 = end_vertex_a->removeArc(start_vertex_a);

  // exit gracefully
  //
  return (return_val1 & return_val2);
}

// end of include file
//
#endif







