// 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 #endif #ifndef ISIP_DOUBLE_LINKED_LIST #include #endif #ifndef ISIP_BOOLEAN #include #endif #ifndef ISIP_COLOR_HASH #include #endif #ifndef ISIP_GRAPH_VERTEX #include #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 ColorHash; template 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 UGraph : public DiGraph { //--------------------------------------------------------------------------- // // 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 (alloc) {} // copy constructor // UGraph(const UGraph& copy_graph) : DiGraph (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! // bool8 assign(const UGraph& copy_graph); // method: operator= // UGraph& operator=(const UGraph& arg) { assign(arg); return *this; } // equality method // bool8 eq(const UGraph& compare_graph) const; //--------------------------------------------------------------------------- // // class-specific public methods: // graph manipulation methods // //--------------------------------------------------------------------------- // method: insertArc // bool8 insertArc(int32 start_index, int32 end_index, bool8 is_epsilon = GraphArc::DEF_EPSILON, float32 weight = GraphArc::DEF_WEIGHT) { return insertArc(getPosition(start_index), getPosition(end_vertex), is_epsilon, weight); } // other arc insertion methods // bool8 insertArc(GraphVertex* start_vertex, GraphVertex* end_vertex, bool8 is_epsilon = GraphArc::DEF_EPSILON, float32 weight = GraphArc::DEF_WEIGHT); // method: removeArc // bool8 removeArc(int32 start_index, int32 end_index) { return removeArc(getPosition(start_index), getPosition(end_vertex)); } // other arc removal methods // bool8 removeArc(GraphVertex* start_vertex, GraphVertex* 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 const String UGraph::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 const String& UGraph::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& copy_graph: (input) the graph to copy // // return: a bool8 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 bool8 UGraph::assign(const UGraph& 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 // int32 old_pos = const_cast& >(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 // int32 num_vertices = arg_a.length() + 2; GraphVertex* copy_sym_ptrs[num_vertices]; GraphVertex* 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& >(arg_a).gotoFirst(); for (int32 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* > (arg_a.getCurr()); // move to the next node in the graph // const_cast& >(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 (int32 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 (int32 i = 0; i < num_vertices; i++) { // get the vertex in question // GraphVertex* tmp_copy_vert = copy_sym_ptrs[i]; // loop over the copy graph's arclist // bool8 arcs_remain = tmp_copy_vert->gotoFirst(); while (arcs_remain) { // setup temporary variables // int32 dest_index = -1; GraphArc* tmp_arc = tmp_copy_vert->getCurr(); GraphVertex* dest_vertex = tmp_arc->getVertex(); // find the destination vertex index // for (int32 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::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& >(arg_a).gotoPosition(old_pos); // exit gracefully // return true; } //------------------------------------------------------------------------- // // required equality method // //------------------------------------------------------------------------- // method: eq // // arguments: // const UGraph& graph: (input) the graph to compare // // return: bool8 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 bool8 UGraph::eq(const UGraph& graph_a) const { // create lists to extract the graph structures // SingleLinkedList this_dlist; SingleLinkedList, Float, Boolean> > this_alist; SingleLinkedList input_dlist; SingleLinkedList, Float, Boolean> > input_alist; // extract the structure of the current list // const_cast* >(this)->get(this_dlist, this_alist); // extract the structure of the input list // const_cast& >(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* start_vertex: (input) the start vertex // GraphVertex* end_vertex: (input) the ending vertex // bool8 is_epsilon: (input) is the arc an epsilon transition // float32 weight: (input) the weight on the arc (if any) // // return: a bool8 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 bool8 UGraph::insertArc(GraphVertex* start_vertex_a, GraphVertex* end_vertex_a, bool8 is_epsilon_a, float32 weight_a) { // declare local variables // bool8 return_val1 = false; bool8 return_val2 = false; // make sure neither vertex is NULL // if ((start_vertex_a == (GraphVertex*)NULL) || (end_vertex_a == (GraphVertex*)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*)NULL) && (start_vertex_a->getParentGraph() != this)) || ((end_vertex_a->getParentGraph() != (UGraph*)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* start_vertex: (input) the start vertex // GraphVertex* end_vertex: (input) the ending vertex // // return: a bool8 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 bool8 UGraph::removeArc(GraphVertex* start_vertex_a, GraphVertex* end_vertex_a) { // make sure neither vertex is NULL // if ((start_vertex_a == (GraphVertex*)NULL) || (end_vertex_a == (GraphVertex*)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 // bool8 return_val1 = start_vertex_a->removeArc(end_vertex_a); // remove the arc form the end to the start vertex // bool8 return_val2 = end_vertex_a->removeArc(start_vertex_a); // exit gracefully // return (return_val1 & return_val2); } // end of include file // #endif