123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- #!/usr/bin/env python
- class Graph:
- def __init__(self, N):
- self.N = N # number of vertices
-
- # maps a vertex number to the list of (vertices, edge value)
- # to which it is connected by edges.
- self.adjacent = [[] for n in xrange(N)]
-
- def connect(self, vertex1, vertex2, edge_value):
- """
- Connect 'vertex1' and 'vertex2' with an edge, with associated
- value 'value'
- """
- # Add vertices to each other's adjacent list
- self.adjacent[vertex1].append( (vertex2, edge_value) )
- self.adjacent[vertex2].append( (vertex1, edge_value) )
- def check(self):
- """
- See if vertex values add up to edge values (mod N).
- """
- for vertex in xrange(self.N):
- for neighbor, edge_value in self.adjacent[vertex]:
- assert (self.vertex_values[vertex] +
- self.vertex_values[neighbor]) % self.N == edge_value
-
- def calc_tree_sizes(self):
- """
- After running this method, the attribute size will contain a list,
- which maps the vertices to the size of the tree that vertex belongs
- to.
- """
- visited = self.N * [-1] # -1 unvisited, otherwise the number of tree
- treenum = 0
-
- # Loop over all vertices, taking unvisited ones as roots.
- for root in xrange(self.N):
- if visited[root] >= 0:
- continue
-
- # explore tree starting at 'root'
- # Stack of vertices to visit, a list of tuples (parent, vertex)
- tovisit = [ (None, root) ]
- while tovisit:
- parent, vertex = tovisit.pop()
- visited[vertex] = treenum
-
- # Loop over adjacent vertices, but skip the vertex we arrived
- # here from the first time it is encountered.
- skip = True
- for neighbor, edge_value in self.adjacent[vertex]:
- if skip and neighbor == parent:
- skip = False
- continue
-
- if visited[neighbor] >= 0:
- # We visited here before, so the graph is cyclic.
- exit('Hmm, graph is cyclic.')
-
- tovisit.append( (vertex, neighbor) )
-
- treenum += 1
-
- # maps the tree number to number of vertices within that tree
- treesizes = treenum * [0]
- for tree in visited:
- treesizes[tree] += 1
-
- self.size = [treesizes[visited[v]] for v in xrange(self.N)]
-
- if verbose:
- freq = (self.N+1) * [0]
- for size in treesizes:
- freq[size] += 1
-
- sys.stderr.write(' Size Trees\n')
- for i, f in enumerate(freq):
- if f:
- sys.stderr.write('%5i %5i\n' % (i, f))
- if i == minsize-1:
- sys.stderr.write('--------------\n')
-
- def write(self, fo, labels = False):
- self.calc_tree_sizes()
-
- fo.write('graph G {\n'
- ' size = "8,8";\n'
- ' edge [color="#ff0000"]\n')
- if labels:
- fo.write(' node [color="#a0e0ee", style=filled];\n')
-
- for vertex, value in enumerate(self.vertex_values):
- if self.size[vertex] < minsize: continue
- fo.write(' { node [label="%i: %i"] v%i }\n' % (
- vertex, value, vertex))
- else:
- fo.write(' node [color="#3377a0", label="",\n'
- ' style=filled, shape=circle]\n')
-
- for vertex in xrange(self.N): # edges
- if self.size[vertex] < minsize: continue
- for neighbor, edge_value in self.adjacent[vertex]:
- if neighbor > vertex: continue
- fo.write(' v%i -- v%i%s;\n' %
- (vertex, neighbor,
- (' [label="%s: %i"]' % (K[edge_value], edge_value))
- if labels else ''))
- fo.write('}\n')
- fo.close()
- if __name__ == '__main__':
- import sys
- from optparse import OptionParser
-
- usage = "usage: %prog [options] [PYCODE]"
-
- description = """\
- Given the python code for a perfect hash function which was generated by
- perfect_hash.py, e.g. by '$ ../perfect_hash.py animals.txt >animals.py',
- this program will create the graph which was used in determining the
- perfect hash function. The input python code may also be given to stdin.
- The output is saved as in the .dot format which is used by the Graphviz
- tools (see http://www.graphviz.org/) to generate a picture of the graph.
- """
- parser = OptionParser(usage = usage,
- description = description,
- prog = sys.argv[0])
- parser.add_option("-l", "--labels",
- action = "store_true",
- help = "Be verbose")
- parser.add_option("-m", "--minsize",
- action = "store",
- default = 1,
- type = "int",
- help = "Include only trees in the output which "
- "have at least INT vertices. "
- "Default is %default, i.e. all trees are "
- "included within the output.",
- metavar = "INT")
- parser.add_option("-o", "--output",
- action = "store",
- help = "Specify output FILE explicitly. "
- "Default, is stdout. ",
- metavar = "FILE")
- parser.add_option("-v", "--verbose",
- action = "store_true",
- help = "Be verbose")
-
- options, args = parser.parse_args()
-
- if options.minsize > 0:
- minsize = options.minsize
- else:
- parser.error("minimal size of trees has to be larger than zero")
-
- verbose = options.verbose
-
- if len(args) > 1:
- parser.error("incorrect number of arguments")
- # --------------------- end parsing and checking -----------------------
-
- if verbose:
- sys.stderr.write('minsize (of trees): %i\n' % minsize)
- sys.stderr.write('labels (in output): %s\n' % options.labels)
- # ------------ input filehandle
-
- if len(args)==1:
- try:
- fi = file(args[0])
- except IOError :
- exit("Error: Can't open `%s' for reading." % args[0])
- else:
- fi = sys.stdin
- # ------------ read input, i.e. execute code
-
- exec(fi.read())
-
- # ------------ make graph
-
- g = Graph(len(G))
- g.vertex_values = G
- for key, hashval in zip(K, H):
- g.connect(hash_f(key, S1),
- hash_f(key, S2),
- hashval)
- g.check()
- # ------------ output filehandle
-
- if options.output:
- try:
- fo = file(options.output, 'w')
- except IOError :
- exit("Error: Can't open `%s' for writing." % options.output)
- else:
- fo = sys.stdout
- # ------------ write output, i.e. generate .dot output
- g.write(fo, options.labels)
- # Local Variables:
- # mode: python
- # End:
|