void test(string lhss, string op, string rhss, string exps) {
    char* l1 = strdup(lhss.c_str());
    char* r1 = strdup(rhss.c_str());
    char* e1 = strdup(exps.c_str());

    V3Number lhnum (new FileLine ("ck",__LINE__), l1);
    V3Number rhnum (new FileLine ("ck",__LINE__), r1);
    V3Number expnum (new FileLine("ck",__LINE__), e1);

    V3Number gotnum (new FileLine("ck",__LINE__), expnum.width());

    if (op=="redOr")	 	gotnum.opRedOr		(lhnum);
    else if (op=="redAnd")	gotnum.opRedAnd		(lhnum);
    else if (op=="redXor")	gotnum.opRedXor		(lhnum);
    else if (op=="redXnor")	gotnum.opRedXnor	(lhnum);
    else if (op=="concat")	gotnum.opConcat		(lhnum,rhnum);
    else if (op=="repl")	gotnum.opRepl		(lhnum,rhnum);
    else if (op=="~")	 	gotnum.opNot		(lhnum);
    else if (op=="!")	 	gotnum.opLogNot		(lhnum);
    else if (op=="negate") 	gotnum.opNegate		(lhnum);
    else if (op=="+")	 	gotnum.opAdd		(lhnum,rhnum);
    else if (op=="-")	 	gotnum.opSub		(lhnum,rhnum);
    else if (op=="*")	 	gotnum.opMul		(lhnum,rhnum);
    else if (op=="/")	 	gotnum.opDiv		(lhnum,rhnum);
    else if (op=="%")	 	gotnum.opModDiv		(lhnum,rhnum);
    else if (op=="&")	 	gotnum.opAnd		(lhnum,rhnum);
    else if (op=="|")	 	gotnum.opOr		(lhnum,rhnum);
    else if (op=="<")	 	gotnum.opLt		(lhnum,rhnum);
    else if (op==">")	 	gotnum.opGt		(lhnum,rhnum);
    else if (op==">>")	 	gotnum.opShiftR		(lhnum,rhnum);
    else if (op=="<<")	 	gotnum.opShiftL		(lhnum,rhnum);
    else if (op=="==")	 	gotnum.opEq		(lhnum,rhnum);
    else if (op=="===")	 	gotnum.opCaseEq		(lhnum,rhnum);
    else if (op=="==?")	 	gotnum.opWildEq		(lhnum,rhnum);
    else if (op=="!=")	 	gotnum.opNeq		(lhnum,rhnum);
    else if (op=="!==")	 	gotnum.opCaseNeq	(lhnum,rhnum);
    else if (op=="!=?")	 	gotnum.opWildNeq	(lhnum,rhnum);
    else if (op=="<=")	 	gotnum.opLte		(lhnum,rhnum);
    else if (op==">=")	 	gotnum.opGte		(lhnum,rhnum);
    else if (op=="&&")	 	gotnum.opLogAnd		(lhnum,rhnum);
    else if (op=="||")	 	gotnum.opLogOr		(lhnum,rhnum);
    else v3fatalSrc("Bad opcode: "<<op);

    UINFO(0,"------- Test:\n"
	  <<"       "<<lhnum<<" "<<op<<endl
	  <<"       "<<rhnum<<endl
	  <<"     = "<<expnum<<endl
	  <<"    =? "<<gotnum<<endl);

    V3Number ok (new FileLine("ck",__LINE__), 1);
    ok.opCaseEq(expnum,gotnum);
    if (ok.toUInt()!=1) {
	v3fatalSrc("%Error:Test FAILED\n");
    }
}
DfaVertex* DfaGraph::findStart() {
    DfaVertex* startp = NULL;
    for (V3GraphVertex* vertexp = this->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
	if (DfaVertex* vvertexp = dynamic_cast<DfaVertex*>(vertexp)) {
	    if (vvertexp->start()) {
		if (startp) v3fatalSrc("Multiple start points in NFA graph");
		startp = vvertexp;
	    }
	} else {
	    v3fatalSrc("Non DfaVertex in DfaGraph\n");
	}
    }
    if (!startp) v3fatalSrc("No start point in NFA graph");
    return startp;
}
    bool compareDfaOrigins(const DfaStates& nfasWithInput, DfaVertex* dfa2p) {
	// Return true if the NFA nodes both DFAs came from are the same list
	// Assume there are no duplicates in either input list or NFAs under dfa2
	nextStep();
	// Mark all input vertexes
	int num1s = 0;
	for (DfaStates::const_iterator nfaIt=nfasWithInput.begin(); nfaIt!=nfasWithInput.end(); ++nfaIt) {
	    DfaVertex* nfaStatep = *nfaIt;
	    nfaStatep->user(m_step);
	    num1s++;
	}
	if (!num1s) v3fatalSrc("DFA node construction that contains no NFA states");

	// Check comparison; must all be marked
	// (Check all in dfa2p were in dfa1p)
	int num2s = 0;
	for (V3GraphEdge* dfaEdgep = dfa2p->outBeginp(); dfaEdgep; dfaEdgep=dfaEdgep->outNextp()) {
	    if (nfaState(dfaEdgep->top())) {
		if (dfaEdgep->top()->user() != m_step) return false;
		num2s++;
	    }
	}
	// If we saw all of the nodes, then they have the same number of hits
	// (Else something in dfa1p that wasn't in dfa2p)
	if (num1s != num2s) return false;
	// Match
	return true;
    }
void LinkCellsGraph::loopsMessageCb(V3GraphVertex* vertexp) {
    if (LinkCellsVertex* vvertexp = dynamic_cast<LinkCellsVertex*>(vertexp)) {
	vvertexp->modp()->v3error("Recursive module (module instantiates itself): "
				  <<vvertexp->modp()->prettyName());
	V3Error::abortIfErrors();
    } else {  // Everything should match above, but...
	v3fatalSrc("Recursive instantiations");
    }
}
Exemple #5
0
void V3Error::init() {
    for (int i=0; i<V3ErrorCode::_ENUM_MAX; i++) {
	s_describedEachWarn[i] = false;
	s_pretendError[i] = V3ErrorCode(i).pretendError();
    }

    if (string(V3ErrorCode(V3ErrorCode::_ENUM_MAX).ascii()) != " MAX") {
	v3fatalSrc("Enum table in V3ErrorCode::EC_ascii() is munged");
    }
}
Exemple #6
0
void V3Graph::test() {
    // Execute all of the tests
    UINFO(2,__FUNCTION__<<": "<<endl);
    { V3GraphTestStrong test; test.run(); }
    { V3GraphTestAcyc test; test.run(); }
    { V3GraphTestVars test; test.run(); }
    { V3GraphTestDfa test; test.run(); }
    { V3GraphTestImport test; test.run(); }
    if (V3GraphTest::debug()) v3fatalSrc("Exiting due to graph testing enabled");
}
void V3Stats::statsReport() {
    UINFO(2,__FUNCTION__<<": "<<endl);

    // Open stats file
    string filename = v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+"__stats.txt";
    ofstream* ofp (V3File::new_ofstream(filename));
    if (ofp->fail()) v3fatalSrc("Can't write "<<filename);

    StatsReport reporter (ofp);

    // Cleanup
    ofp->close(); delete ofp; VL_DANGLING(ofp);
}
Exemple #8
0
    void lifeToAbove() {
	// Any varrefs under a if/else branch affect statements outside and after the if/else
	if (!m_aboveLifep) v3fatalSrc("Pushing life when already at the top level");
	for (LifeMap::iterator it = m_map.begin(); it!=m_map.end(); ++it) {
	    AstVarScope* nodep = it->first;
	    m_aboveLifep->complexAssignFind(nodep);
	    if (it->second.everSet()) {
		// Record there may be an assignment, so we don't constant propagate across the if.
		complexAssignFind(nodep);
	    } else {
		// Record consumption, so we don't eliminate earlier assignments
		consumedFind(nodep);
	    }
	}
    }
Exemple #9
0
void V3TSP::selfTestString() {
    typedef TspGraphTmpl<string> Graph;
    Graph graph;
    graph.addVertex("0");
    graph.addVertex("1");
    graph.addVertex("2");
    graph.addVertex("3");

    graph.addEdge("0", "1", 3943);
    graph.addEdge("0", "2", 3456);
    graph.addEdge("0", "3", 4920);
    graph.addEdge("1", "2", 2730);
    graph.addEdge("1", "3", 8199);
    graph.addEdge("2", "3", 4130);

    Graph minGraph;
    graph.makeMinSpanningTree(&minGraph);
    if (debug() >= 6) minGraph.dumpGraphFilePrefixed("minGraph");

    std::vector<string> oddDegree = minGraph.getOddDegreeKeys();
    Graph matching;
    graph.perfectMatching(oddDegree, &matching);
    if (debug() >= 6) matching.dumpGraphFilePrefixed("matching");

    minGraph.combineGraph(matching);

    std::vector<string> result;
    minGraph.findEulerTour(&result);

    std::vector<string> expect;
    expect.push_back("0");
    expect.push_back("2");
    expect.push_back("1");
    expect.push_back("2");
    expect.push_back("3");

    if (expect != result) {
        for (std::vector<string>::iterator it = result.begin(); it != result.end(); ++it) {
            cout<<*it<<" ";
        }
        cout<<endl;
        v3fatalSrc("TSP string self-test fail. Result (above) did not match expectation.");
    }
}
    void add_complement_edges() {
	// Find accepting vertex
	DfaVertex* acceptp = NULL;
	for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
	    if (DfaVertex* vvertexp = dynamic_cast<DfaVertex*>(vertexp)) {
		if (vvertexp->accepting()) {
		    acceptp = vvertexp;
		    break;
		}
	    }
	}
	if (!acceptp) v3fatalSrc("No accepting vertex in DFA\n");

	// Remap edges
	for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
	    if (DfaVertex* vvertexp = dynamic_cast<DfaVertex*>(vertexp)) {
		//UINFO(9, "   on vertex "<<vvertexp->name()<<endl);
		if (!vvertexp->accepting() && vvertexp != m_tempNewerReject) {
		    for (V3GraphEdge* nextp, *edgep = vertexp->outBeginp(); edgep; edgep=nextp) {
			nextp = edgep->outNextp();
			if (!edgep->user()) { // Not processed
			    // Old edges to accept now go to new reject
			    DfaEdge* vedgep = static_cast<DfaEdge*>(edgep);
			    DfaVertex* tovertexp = static_cast<DfaVertex*>(edgep->top());
			    if (tovertexp->accepting()) {
				new DfaEdge(graphp(), vvertexp, m_tempNewerReject, vedgep);
				edgep->unlinkDelete(); edgep=NULL;
			    }

			    // NOT of all values goes to accept
			    // We make a edge for each value to OR, IE
			    // edge(complemented,a) edge(complemented,b) means !(a | b)
			    if (!tovertexp->accepting()) {  // Note we must include edges moved above to reject
				DfaEdge* newp = new DfaEdge (graphp(), vvertexp, acceptp, vedgep);
				newp->complement(!newp->complement());
				newp->user(1);
			    }
			}
		    }
		}
	    }
	}
    }
    uint32_t hashDfaOrigins(DfaVertex* dfaStatep) {
	// Find the NFA states this dfa came from,
	// Record a checksum, so we can search for it later by the list of nfa nodes.
	// The order of the nodes is not deterministic; the hash thus must not depend on order of edges
	uint32_t hash = 0;
	// Foreach NFA state (this DFA state was formed from)
	if (debug()) nextStep();
	for (V3GraphEdge* dfaEdgep = dfaStatep->outBeginp(); dfaEdgep; dfaEdgep=dfaEdgep->outNextp()) {
	    if (nfaState(dfaEdgep->top())) {
		DfaVertex* nfaStatep = static_cast<DfaVertex*>(dfaEdgep->top());
		hash ^= hashVertex(nfaStatep);
		if (debug()) {
		    if (nfaStatep->user()==m_step) v3fatalSrc("DFA state points to duplicate NFA state.");
		    nfaStatep->user(m_step);
		}
	    }
	}
	return hash;
    }
Exemple #12
0
void V3Hashed::dumpFile(const string& filename, bool tree) {
    const VL_UNIQUE_PTR<ofstream> logp (V3File::new_ofstream(filename));
    if (logp->fail()) v3fatalSrc("Can't write "<<filename);

    map<int,int> dist;

    V3Hash lasthash;
    int num_in_bucket = 0;
    for (HashMmap::iterator it=begin(); 1; ++it) {
	if (lasthash != it->first || it==end()) {
	    if (it!=end()) lasthash = it->first;
	    if (num_in_bucket) {
		if (dist.find(num_in_bucket)==dist.end()) {
		    dist.insert(make_pair(num_in_bucket,1));
		} else {
		    ++dist[num_in_bucket];
		}
	    }
	    num_in_bucket = 0;
	}
	if (it==end()) break;
	num_in_bucket++;
    }
    *logp <<"\n*** STATS:\n"<<endl;
    *logp<<"    #InBucket   Occurrences\n";
    for (map<int,int>::iterator it=dist.begin(); it!=dist.end(); ++it) {
	*logp<<"    "<<setw(9)<<it->first<<"  "<<setw(12)<<it->second<<endl;
    }

    *logp <<"\n*** Dump:\n"<<endl;
    for (HashMmap::iterator it=begin(); it!=end(); ++it) {
	if (lasthash != it->first) {
	    lasthash = it->first;
	    *logp <<"    "<<it->first<<endl;
	}
	*logp <<"\t"<<it->second<<endl;
	// Dumping the entire tree may make nearly N^2 sized dumps,
	// because the nodes under this one may also be in the hash table!
	if (tree) it->second->dumpTree(*logp,"\t\t");
    }
}
    void optimize_no_outbound() {
	// Non-accepting states with no outbound transitions may be
	// deleted.  Then, any arcs feeding those states, and perhaps those
	// states...

	// Vertex::m_user begin: 1 indicates on the work list
	// (Otherwise we might have nodes on the list twice, and reference after deleting them.)
	m_graphp->userClearVertices();

	// Find all dead vertexes
	stack<DfaVertex*> workps;
	for (V3GraphVertex* vertexp = m_graphp->verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
	    if (DfaVertex* vvertexp = dynamic_cast<DfaVertex*>(vertexp)) {
		workps.push(vvertexp);
		vertexp->user(1);
	    } else {
		// If ever remove this, need dyn cast below
		v3fatalSrc("Non DfaVertex in dfa graph");
	    }
	}

	// While deadness... Delete and find new dead nodes.
	while (!workps.empty()) {
	    DfaVertex* vertexp = workps.top(); workps.pop();
	    vertexp->user(0);
	    if (isDead(vertexp)) {
		// Add nodes that go here to the work list
		for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep=edgep->inNextp()) {
		    DfaVertex* fromvertexp = static_cast<DfaVertex*>(edgep->fromp());
		    if (fromvertexp != vertexp
			&& !fromvertexp->user()) {
			workps.push(static_cast<DfaVertex*>(fromvertexp));
			fromvertexp->user(1);
		    }
		}
		// Transitions to this state removed by the unlink function
		vertexp->unlinkDelete(m_graphp); vertexp=NULL;
	    }
	}
    }
Exemple #14
0
    void simplifyPli() {
	// For now, we'll not gate any logic with PLI lvariables in it.  In
	// the future we may move PLI statements.  One way to do so is to
	// all below pli VARs to go IfVertex -> PliVertex -> VarVertex then
	// they all must get colored the same. There may be a lot of
	// duplicated edges to the PLI; they'll need cleanup.  All of the
	// later optimizations need to deal, and the GaterBodyVisitor needs
	// to know how to move them.
	if (m_pliVertexp) {
	    // Follow PLI out edges to find all relevant variables
	    for (V3GraphEdge* nextp,* edgep = m_pliVertexp->outBeginp(); edgep; edgep = nextp) {
		nextp = edgep->outNextp();  // We may edit the list
		if (GaterVarVertex* vVxp = dynamic_cast<GaterVarVertex*>(edgep->top())) {
		    vVxp->unlinkDelete(&m_graph); vVxp=NULL; edgep=NULL;
		} else {
		    m_graph.dump();
		    v3fatalSrc("PLI vertex points to non-signal");
		}
	    }
	    m_pliVertexp->unlinkDelete(&m_graph); m_pliVertexp = NULL;
	}
    }
string AstVar::vlArgType(bool named, bool forReturn, bool forFunc) const {
    if (forReturn) named=false;
    if (forReturn) v3fatalSrc("verilator internal data is never passed as return, but as first argument");
    string arg;
    if (isWide() && isInOnly()) arg += "const ";
    AstBasicDType* bdtypep = basicp();
    bool strtype = bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::STRING;
    if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::CHARPTR) {
	arg += "const char*";
    } else if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::SCOPEPTR) {
	arg += "const VerilatedScope*";
    } else if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::DOUBLE) {
	arg += "double";
    } else if (bdtypep && bdtypep->keyword()==AstBasicDTypeKwd::FLOAT) {
	arg += "float";
    } else if (strtype) {
	if (isInOnly()) arg += "const ";
	arg += "string";
    } else if (widthMin() <= 8) {
	arg += "CData";
    } else if (widthMin() <= 16) {
	arg += "SData";
    } else if (widthMin() <= VL_WORDSIZE) {
	arg += "IData";
    } else if (isQuad()) {
	arg += "QData";
    } else if (isWide()) {
	arg += "WData";  // []'s added later
    }
    if (isWide() && !strtype) {
	arg += " (& "+name();
	arg += ")["+cvtToStr(widthWords())+"]";
    } else {
	if (forFunc && (isOutput() || (strtype && isInput()))) arg += "&";
	if (named) arg += " "+name();
    }
    return arg;
}
Exemple #16
0
    void scoreboardPopStmt() {
	//UINFO(9,"    pop"<<endl);
	if (m_stmtStackps.empty()) v3fatalSrc("Stack underflow");
	m_stmtStackps.pop_back();
    }
Exemple #17
0
    static bool followCyclic(const V3GraphEdge* edgep) {
	const SplitEdge* oedgep = dynamic_cast<const SplitEdge*>(edgep);
	if (!oedgep) v3fatalSrc("Following edge of non-SplitEdge type");
	if (oedgep->ignoreThisStep()) return false;
	return true;
    }
Exemple #18
0
    void findEulerTourRecurse(vl_unordered_set<unsigned>* markedEdgesp,
                              Vertex* startp,
                              std::vector<T_Key>* sortedOutp) {
        Vertex* cur_vertexp = startp;

        // Go on a random tour. Fun!
        std::vector<Vertex*> tour;
        do {
            UINFO(6, "Adding "<<cur_vertexp->key()<<" to tour.\n");
            tour.push_back(cur_vertexp);

            // Look for an arbitrary edge we've not yet marked
            for (V3GraphEdge* edgep = cur_vertexp->outBeginp();
                 edgep; edgep = edgep->outNextp()) {
                vluint32_t edgeId = edgep->user();
                if (markedEdgesp->end() == markedEdgesp->find(edgeId)) {
                    // This edge is not yet marked, so follow it.
                    markedEdgesp->insert(edgeId);
                    Vertex* neighborp = castVertexp(edgep->top());
                    UINFO(6, "following edge "<<edgeId
                          <<" from "<<cur_vertexp->key()
                          <<" to "<<neighborp->key()<<endl);
                    cur_vertexp = neighborp;
                    goto found;
                }
            }
            v3fatalSrc("No unmarked edges found in tour");
          found:
            ;
        } while (cur_vertexp != startp);
        UINFO(6, "stopped, got back to start of tour @ "<<cur_vertexp->key()<<endl);

        // Look for nodes on the tour that still have
        // un-marked edges. If we find one, recurse.
        for (typename std::vector<Vertex*>::iterator it = tour.begin();
             it != tour.end(); ++it) {
            Vertex* vxp = *it;
            bool recursed;
            do {
                recursed = false;
                // Look for an arbitrary edge at vxp we've not yet marked
                for (V3GraphEdge* edgep = vxp->outBeginp();
                     edgep; edgep = edgep->outNextp()) {
                    vluint32_t edgeId = edgep->user();
                    if (markedEdgesp->end() == markedEdgesp->find(edgeId)) {
                        UINFO(6, "Recursing.\n");
                        findEulerTourRecurse(markedEdgesp, vxp, sortedOutp);
                        recursed = true;
                        goto recursed;
                    }
                }
              recursed:
                ;
            } while (recursed);
            sortedOutp->push_back(vxp->key());
        }

        UINFO(6, "Tour was: ");
        for (typename std::vector<Vertex*>::iterator it = tour.begin();
             it != tour.end(); ++it) {
            Vertex* vxp = *it;
            UINFONL(6, " "<<vxp->key());
        }
        UINFONL(6, "\n");
    }
Exemple #19
0
void V3TSP::selfTestStates() {
    // Linear test -- coords all along the x-axis
    {
        V3TSP::StateVec states;
        TspTestState s10(10,0);
        TspTestState s60(60,0);
        TspTestState s20(20,0);
        TspTestState s100(100,0);
        TspTestState s5(5,0);
        states.push_back(&s10);
        states.push_back(&s60);
        states.push_back(&s20);
        states.push_back(&s100);
        states.push_back(&s5);

        V3TSP::StateVec result;
        tspSort(states, &result);

        V3TSP::StateVec expect;
        expect.push_back(&s100);
        expect.push_back(&s60);
        expect.push_back(&s20);
        expect.push_back(&s10);
        expect.push_back(&s5);
        if (expect != result) {
            for (V3TSP::StateVec::iterator it = result.begin();
                 it != result.end(); ++it) {
                const TspTestState* statep = dynamic_cast<const TspTestState*>(*it);
                cout<<statep->xpos()<<" ";
            }
            cout<<endl;
            v3fatalSrc("TSP linear self-test fail. Result (above) did not match expectation.");
        }
    }

    // Second test. Coords are distributed in 2D space.
    // Test that tspSort() will rotate the list for minimum cost.
    {
        V3TSP::StateVec states;
        TspTestState a(0,0);
        TspTestState b(100,0);
        TspTestState c(200,0);
        TspTestState d(200,100);
        TspTestState e(150,150);
        TspTestState f(0,150);
        TspTestState g(0,100);

        states.push_back(&a);
        states.push_back(&b);
        states.push_back(&c);
        states.push_back(&d);
        states.push_back(&e);
        states.push_back(&f);
        states.push_back(&g);

        V3TSP::StateVec result;
        tspSort(states, &result);

        V3TSP::StateVec expect;
        expect.push_back(&f);
        expect.push_back(&g);
        expect.push_back(&a);
        expect.push_back(&b);
        expect.push_back(&c);
        expect.push_back(&d);
        expect.push_back(&e);

        if (expect != result) {
            for (V3TSP::StateVec::iterator it = result.begin();
                 it != result.end(); ++it) {
                const TspTestState* statep = dynamic_cast<const TspTestState*>(*it);
                cout<<statep->xpos()<<","<<statep->ypos()<<" ";
            }
            cout<<endl;
            v3fatalSrc("TSP 2d cycle=false self-test fail. Result (above) did not match expectation.");
        }
    }
}
Exemple #20
0
void V3Graph::dumpDotFile(const string& filename, bool colorAsSubgraph) {
    // This generates a file used by graphviz, http://www.graphviz.org
    // "hardcoded" parameters:
    const auto_ptr<ofstream> logp (V3File::new_ofstream(filename));
    if (logp->fail()) v3fatalSrc("Can't write "<<filename);

    // Header
    *logp<<"digraph v3graph {\n";
    *logp<<"\tgraph\t[label=\""<<filename<<"\",\n";
    *logp<<"\t\t labelloc=t, labeljust=l,\n";
    *logp<<"\t\t //size="<<"\"7.5,10\","<<"\n";
    *logp<<"\t\t rankdir="<<dotRankDir()<<"];\n";

    // List of all possible subgraphs
    typedef multimap<string,V3GraphVertex*> SubgraphMmap;
    SubgraphMmap subgraphs;
    for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
	string vertexSubgraph = (colorAsSubgraph && vertexp->color()) ? cvtToStr(vertexp->color()) : "";
	subgraphs.insert(make_pair(vertexSubgraph, vertexp));
    }

    // We use a map here, as we don't want to corrupt anything (userp) in the graph,
    // and we don't care if this is slow.
    map<V3GraphVertex*,int>  numMap;

    // Print vertices
    int n=0;
    string subgr;
    for (SubgraphMmap::iterator it = subgraphs.begin(); it!=subgraphs.end(); ++it) {
	string vertexSubgraph = it->first;
	V3GraphVertex* vertexp = it->second;
	numMap[vertexp] = n;
	if (subgr != vertexSubgraph) {
	    if (subgr!="") *logp<<"\t};\n";
	    subgr = vertexSubgraph;
	    if (subgr!="") *logp<<"\tsubgraph cluster_"<<subgr<<" {\n";
	}
	if (subgr!="") *logp<<"\t";
	*logp<<"\tn"<<vertexp->dotName()<<(n++)
	     <<"\t[fontsize=8 "
	     <<"label=\""<<(vertexp->name()!="" ? vertexp->name() : "\\N");
	if (vertexp->rank()) *logp<<" r"<<vertexp->rank();
	if (vertexp->fanout()) *logp<<" f"<<vertexp->fanout();
	if (vertexp->color()) *logp<<"\\n c"<<vertexp->color();
	*logp<<"\"";
	*logp<<", color="<<vertexp->dotColor();
	if (vertexp->dotStyle()!="") *logp<<", style="<<vertexp->dotStyle();
	if (vertexp->dotShape()!="") *logp<<", shape="<<vertexp->dotShape();
	*logp<<"];\n";
    }
    if (subgr!="") *logp<<"\t};\n";

    // Print edges
    for (V3GraphVertex* vertexp = verticesBeginp(); vertexp; vertexp=vertexp->verticesNextp()) {
	for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
	    if (edgep->weight()) {
		int fromVnum = numMap[edgep->fromp()];
		int toVnum   = numMap[edgep->top()];
		*logp<<"\tn"<<edgep->fromp()->dotName()<<fromVnum
		     <<" -> n"<<edgep->top()->dotName()<<toVnum
		     <<" ["
		    //<<"fontsize=8 label=\""<<(edgep->name()!="" ? edgep->name() : "\\E")<<"\""
		     <<"fontsize=8 label=\""<<(edgep->dotLabel()!="" ? edgep->dotLabel() : "")<<"\""
		     <<" weight="<<edgep->weight()
		     <<" color="<<edgep->dotColor();
		if (edgep->dotStyle()!="") *logp<<" style="<<edgep->dotStyle();
		//if (edgep->cutable()) { *logp<<",constraint=false"; }    // to rank without following edges
		*logp<<"];\n";
	    }
	}
    }
    // Vertex::m_user end, now unused

    // Trailer
    *logp << "}\n";
    logp->close();

    cout << "dot -Tpdf -o ~/a.pdf "<<filename<<endl;
}