#ifndef GRAPH_H #define GRAPH_H /////////////////////////////////////////////////////////////// // Graph.h - Graph Library // // Ver 1.3 // // Language: Visual C++ 2012 // // Platform: Lenova ThinkPad E420s, Windows 7 // // Application: Help for CSE687 - OOD, Pr#1, Spring 2013 // // Author: Jim Fawcett, CST 4-187, Syracuse University // // (315) 443-3948, jfawcett@twcny.rr.com // /////////////////////////////////////////////////////////////// /* Package Operations: =================== A graph is a collection of vertices, connected by edges. Each vertex holds some kind of information held in an instance of a type V and an integer id, which should be unique with a given graph. Each edge connects a parent vertex with a child vertex and it too holds information in an instance of a type E. This package provides two classes that support creation of instances of a graph: Vertex and Graph. These are template-based with parameters V and E, as described above. The graph holds a composed collection of vertices. Each vertex may hold one or more edges. The edge is implemented as an instance of std::pair where the first parameter is the id of the child vertex and the second parameter is an instance of the edge information type E. Note: It is important that vertex ids be unique. If you set any of them with the second argument of the Vertex constructor or with the id() function it is up to you to ensure uniqueness. If you don't explicitly set any ids then the constructor insures that vertices get sequential ids in the order constructed. Maintanence Information: ======================== Required files: --------------- Graph.h, Graph.cpp, XmlReader.h, XmlReader.cpp, XmlWriter.h, XmlWriter.cpp Build Process: -------------- Using Visual Studio Command Prompt: devenv Graph.sln /rebuild debug Revision History: ----------------- - ver 1.3 : added GraphToXmlString and GraphFromXmlString functions - ver 1.2 : changed id() return to reference so can be changed - ver 1.1 : added prologue comments, compile test directive - ver 1.0 : first release */ /* * To Do: * - create graph instance from XML * - create XML representation from graph instance * - Strong Components, Topological Sorting * - Find disconnected graph partitions */ #include #include #include #include "XmlWriter.h" #include "XmlReader.h" #include #include namespace GraphLib { /////////////////////////////////////////////////////////////// // Vertex class template class Vertex { public: typedef std::pair Edge; // graph index of target vertex, edge type typename typedef std::vector::iterator iterator; iterator begin(); iterator end(); Vertex(V v, int id=-1); void add(Edge& edge); // Vertex(const Vertex& v); compiler generated is correct // Vertex& operator=(const Vertex& v); compiler generated is correct Edge& operator[](size_t i); Edge operator[](size_t i) const; V& value(); size_t& id(); size_t size(); bool& mark(); private: std::vector _edges; V _v; size_t _id; static size_t count; bool _mark; }; //----< reserve memory for, and initialize, static count >----- template size_t Vertex::count = 0; //----< set and return boolean mark, used for traversal >------ template bool& Vertex::mark() { return _mark; } //----< return iterator pointing to first edge >--------------- template typename Vertex::iterator Vertex::begin() { return _edges.begin(); } //----< return iterator pointing to one past last edge >------- template typename Vertex::iterator Vertex::end() { return _edges.end(); } //----< construct instance >----------------------------------- template Vertex::Vertex(V v, int id) : _v(v), _id(count++), _mark(false) { if(id != -1) _id = id; } //----< add edge to vertex edge collection >------------------- template void Vertex::add(Edge& edge) { _edges.push_back(edge); } //----< index non-const vertex's edges >----------------------- template typename Vertex::Edge& Vertex::operator[](size_t i) { return _edges[i]; } //----< index const vertex's edges >--------------------------- template typename Vertex::Edge Vertex::operator[](size_t i) const { return _edges[i]; } //----< set and read value of vertex's held type, V >---------- template V& Vertex::value() { return _v; } //----< return vertex's id >----------------------------------- template size_t& Vertex::id() { return _id; } //----< return number of edges >------------------------------- template size_t Vertex::size() { return _edges.size(); } /////////////////////////////////////////////////////////////// // Graph class template class Graph { public: typename typedef std::vector< Vertex >::iterator iterator; iterator begin(); iterator end(); // Graph(const Graph& g); compiler generated is correct // Graph& operator=(const Graph& g); compiler generated is correct Vertex& operator[](size_t i); Vertex operator[](size_t i) const; void addVertex(Vertex v); void addEdge(E eval, Vertex& parent, Vertex& child); size_t findVertexIndexById(size_t id); size_t size(); void dfs(Vertex& v, void(*f)(Vertex& v)); private: std::vector< Vertex > adj; std::unordered_map idMap; // id maps to graph index void dfsCore(Vertex& v, void(*f)(Vertex& v)); }; //----< return iterator pointing to first vertex >------------- template typename Graph::iterator Graph::begin() { return adj.begin(); } //----< return iterator pointing one past last vertex >-------- template typename Graph::iterator Graph::end() { return adj.end(); } //----< index non-const graph's vertex collection >------------ template typename Vertex& Graph::operator[](size_t i) { return adj[i]; } //----< index const graph's vertex collection >---------------- template typename Vertex Graph::operator[](size_t i) const { return adj[i]; } //----< add vertex to graph's vertex collection >-------------- template void Graph::addVertex(Vertex v) { adj.push_back(v); idMap[v.id()] = adj.size() - 1; } //----< return number of vertices in graph's collection >------ template size_t Graph::size() { return adj.size(); } //----< return index of vertex with specified id >------------- template size_t Graph::findVertexIndexById(size_t id) { return idMap[id]; } //----< add edge from specified parent to child vertices >----- template void Graph::addEdge(E eVal, Vertex& parent, Vertex& child) { size_t childIndex = findVertexIndexById(child.id()); if(childIndex == adj.size()) throw std::exception("no edge child"); size_t parentIndex = findVertexIndexById(parent.id()); if(parentIndex == adj.size()) throw std::exception("no edge parent"); Vertex::Edge e; e.first = childIndex; e.second = eVal; adj[parentIndex].add(e); } //----< recursive depth first search with action f >----------- template void Graph::dfsCore(Vertex& v, void(*f)(Vertex& cv)) { f(v); v.mark() = true; for(auto edge : v) { if(adj[edge.first].mark() == false) dfsCore(adj[edge.first], f); } for(auto& vert : adj) { if(vert.mark() == false) dfsCore(vert, f); } } //----< depth first search, clears marks for next search >----- template void Graph::dfs(Vertex& v, void(*f)(Vertex& cv)) { dfsCore(v, f); for(auto& vert : adj) vert.mark() = false; } /////////////////////////////////////////////////////////////// // Display class template class Display { public: typedef Graph graph; typedef Vertex vertex; typedef std::pair edge; static std::vector< Vertex > vertsWithNoParents(graph& g) { std::vector parentCount; graph::iterator iter = g.begin(); while(iter != g.end()) { parentCount.push_back(0); ++iter; } iter = g.begin(); while(iter != g.end()) { vertex v = *iter; for(size_t i=0; i noParents; for(size_t j=0; j 1) { Vertex vert(elems[1].second); vert.id() = ToInt(elems[0].second); gr.addVertex(vert); } else { throw std::exception("ill-formed graph XML"); } } } // add edges XmlReader erdr(Xml); int parentId; while(erdr.next()) { if(erdr.tag() == "vertex") { XmlReader::attribElems elems = erdr.attributes(); if(elems.size() > 0) { parentId = ToInt(elems[0].second); } else { throw std::exception("ill-formed graph XML"); } } if(erdr.tag() == "edge") { XmlReader::attribElems eelems = erdr.attributes(); if(eelems.size() > 1) { int childId = ToInt(eelems[0].second); E eval = eelems[1].second; int parentIndex = gr.findVertexIndexById(parentId); Vertex parent = gr[parentIndex]; int childIndex = gr.findVertexIndexById(childId); Vertex child = gr[childIndex]; gr.addEdge(eval, parent, child); } else { throw std::exception("ill-formed graph XML"); } } } } //----< serialize graph to XML string >---------------------- template std::string GraphToXmlString(Graph& gr) { XmlWriter wrg; wrg.start("graph"); XmlWriter wrv; for(auto vert : gr) { wrv.start("vertex"); wrv.addAttribute("id", ToString(vert.id())); wrv.addAttribute("value", vert.value()); XmlWriter wre; for(auto edge : vert) { wre.start("edge"); wre.addAttribute("targetId", ToString(gr[edge.first].id())); wre.addAttribute("value", edge.second); wre.end(); } wrv.addBody(wre.xml()); wrv.end(); } wrg.addBody(wrv.xml()); wrg.end(); return wrg.xml(); } //----< convert Xml string to indented form >---------------- std::string indent(std::string Xml) { std::string indented; size_t level = 0; size_t pos = 0; char lastXmlMarkup; for(size_t pos = 0; pos < Xml.size(); pos++) { switch(Xml[pos]) { case '<' : lastXmlMarkup = '<'; break; case '/' : lastXmlMarkup = '/'; break; } if(Xml[pos] == '<' && Xml[pos+1] != '/') { if(pos > 0) { ++level; indented += "\n"; } for(size_t i=0; i' && lastXmlMarkup == '/') { --level; indented += Xml[pos]; lastXmlMarkup = ' '; } else indented += Xml[pos]; } return indented; } } #endif