217 lines
7.3 KiB
Python
217 lines
7.3 KiB
Python
#!/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:
|