mirror of
https://github.com/RT-Thread/rt-thread.git
synced 2026-09-20 00:45:22 +08:00
sync with rtgui-0.6
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
|
||||
perfect_hash.py
|
||||
|
||||
Ilan Schnell <ilanschnell@gmail.com>, 2008
|
||||
|
||||
|
||||
Generate a minimal perfect hash function for the keys in a file,
|
||||
desired hash values may be specified within this file as well.
|
||||
A given code template is filled with parameters, such that the
|
||||
output is code which implements the hash function.
|
||||
Templates can easily be constructed for any programming language.
|
||||
|
||||
The code is based on an a program A.M. Kuchling wrote:
|
||||
http://www.amk.ca/python/code/perfect-hash
|
||||
|
||||
The algorithm the program uses is described in the paper
|
||||
'Optimal algorithms for minimal perfect hashing',
|
||||
Z. J. Czech, G. Havas and B.S. Majewski.
|
||||
http://citeseer.ist.psu.edu/122364.html
|
||||
|
||||
|
||||
Content:
|
||||
|
||||
perfect_hash.py The actual program, try $ ./perfect_hash.py animals.txt
|
||||
|
||||
doc HTML and plain text documentation
|
||||
|
||||
example1-C An example which shows how to generate a C program
|
||||
which implements a perfect hash table.
|
||||
|
||||
example2-C Another example in C.
|
||||
|
||||
example-C++ In this C++ example, a class is used to define the
|
||||
interface to a static lookup table.
|
||||
|
||||
example-PyModule A lookup table in implemented as a C extension module
|
||||
for Python.
|
||||
|
||||
example-Python Some small Python programs which show how to access
|
||||
some functions and classes in perfect_hash.py directly,
|
||||
i.e. using perfect_hash has a Python module, rather
|
||||
than a standalone program.
|
||||
|
||||
graph A small program `py2dot' which converts the default python
|
||||
output code from perfect_hash.py into a .dot-file which
|
||||
can be used by Graphviz (see http://www.graphviz.org/) to
|
||||
create a picture of the graph.
|
||||
|
||||
run Runs some tests.
|
||||
@@ -0,0 +1,7 @@
|
||||
# 'animals.txt'
|
||||
Elephant
|
||||
Horse
|
||||
Camel
|
||||
Python
|
||||
Dog
|
||||
Cat
|
||||
@@ -0,0 +1,22 @@
|
||||
|
||||
|
||||
all: doc.html doc.txt
|
||||
|
||||
|
||||
doc.html: doc.in parameters.txt mktable.py
|
||||
./mktable.py >table.html
|
||||
markdown doc.in | \
|
||||
sed -e "s,<h6>table</h6>,m4_include(\`table.html')," | \
|
||||
m4 -P >doc.html
|
||||
rm table.html
|
||||
|
||||
|
||||
doc.txt: doc.in parameters.txt
|
||||
sed <doc.in -e "s,###### table,m4_include(\`parameters.txt')," \
|
||||
-e "s,\`\([^\`]*\)\`,'\1',g" | m4 -P >doc.txt
|
||||
|
||||
|
||||
clean:
|
||||
rm doc.html doc.txt
|
||||
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,136 @@
|
||||
|
||||
|
||||
<h2>Generic Perfect Hash Generator</h2>
|
||||
<p><em>Ilan Schnell, 2008</em>
|
||||
</p>
|
||||
<p>perfect_hash.py provides a perfect hash generator which is not language
|
||||
specific. That is, the generator can output a perfect hash function for
|
||||
a given set of keys in any programming language, this is achieved by
|
||||
filling a given code template.
|
||||
</p>
|
||||
|
||||
<h3>Acknowledgments:</h3>
|
||||
<p>This code is derived from A. M. Kuchling's
|
||||
<a href="http://www.amk.ca/python/code/perfect-hash">Perfect Minimal Hash Generator</a>.
|
||||
</p>
|
||||
|
||||
<h3>Introduction:</h3>
|
||||
<p>A perfect hash function of a certain set S of keys is a hash function
|
||||
which maps all keys in S to different numbers.
|
||||
That means that for the set S,
|
||||
the hash function is collision-free, or perfect.
|
||||
Further, a perfect hash function is called minimal when it maps n keys
|
||||
to n <em>consecutive</em> integers, usually in the range from 0 to n-1.
|
||||
</p>
|
||||
<p>After coming across A. M. Kuchling's Perfect Minimal Hash Generator,
|
||||
I decided to write a general tool for generating perfect hashes.
|
||||
It is general in the sense that it can produce perfect hash functions
|
||||
for almost any programming language.
|
||||
A given code template is filled with parameters,
|
||||
such that the output is code which implements the hash function.
|
||||
</p>
|
||||
<p>The algorithm the program uses is described in the paper
|
||||
<a href="http://citeseer.ist.psu.edu/122364.html">"Optimal algorithms for minimal perfect hashing"</a>,
|
||||
Z. J. Czech, G. Havas and B.S. Majewski.
|
||||
</p>
|
||||
<p>I tried to illustrate the algorithm and explain how it works on
|
||||
<a href="http://ilan.schnell-web.net/prog/perfect-hash/algo.html">this page</a>.
|
||||
</p>
|
||||
|
||||
<h3>Usage:</h3>
|
||||
<p>Given a set of keys which are ordinary character string,
|
||||
the program returns a minimal perfect hash function.
|
||||
This hash function is returned in the form of Python code by default.
|
||||
Suppose we have a file with keys:
|
||||
</p>
|
||||
<pre><code># 'animals.txt'
|
||||
Elephant
|
||||
Horse
|
||||
Camel
|
||||
Python
|
||||
Dog
|
||||
Cat
|
||||
</code></pre><p>The exact way this file is parsed can be specified using command line
|
||||
options, for example it is possible to only read one column from a file
|
||||
which contains different items in each row.
|
||||
The program is invoked like this:
|
||||
</p>
|
||||
<pre><code># =======================================================================
|
||||
# ================= Python code for perfect hash function ===============
|
||||
# =======================================================================
|
||||
|
||||
G = [0, 0, 4, 1, 0, 3, 8, 1, 6]
|
||||
|
||||
S1 = [5, 0, 0, 6, 1, 0, 4, 7]
|
||||
S2 = [7, 3, 6, 7, 8, 5, 7, 6]
|
||||
|
||||
def hash_f(key, T):
|
||||
return sum(T[i % 8] * ord(c) for i, c in enumerate(str(key))) % 9
|
||||
|
||||
def perfect_hash(key):
|
||||
return (G[hash_f(key, S1)] + G[hash_f(key, S2)]) % 9
|
||||
|
||||
# ============================ Sanity check =============================
|
||||
|
||||
K = ["Elephant", "Horse", "Camel", "Python", "Dog", "Cat"]
|
||||
H = [0, 1, 2, 3, 4, 5]
|
||||
|
||||
assert len(K) == len(H) == 6
|
||||
|
||||
for k, h in zip(K, H):
|
||||
assert perfect_hash(k) == h
|
||||
</code></pre><p>The way the program works is by filling a code template with the calculated
|
||||
parameters. The program can take such a template in form of a file and
|
||||
fill in the calculated parameters, this allows the generation of perfect
|
||||
hash function in any programming language. The hash function is kept quite
|
||||
simple and does not require machine or language specific byte level operations
|
||||
which might be hard to implement in the target language.
|
||||
The following parameters are available in the template, and will expand to:
|
||||
</p>
|
||||
<table>
|
||||
<tr><th>string</th><th>expands to</th></tr>
|
||||
<tr><td><code>$NS</code></td><td>the length of S1 and S2</td></tr>
|
||||
<tr><td><code>$S1</code></td><td>array of integers S1</td></tr>
|
||||
<tr><td><code>$S2</code></td><td>array of integers S2</td></tr>
|
||||
<tr><td><code>$NG</code></td><td>length of array G</td></tr>
|
||||
<tr><td><code>$G</code></td><td>array of integers G</td></tr>
|
||||
<tr><td><code>$NK</code></td><td>the number of keys, i.e. length of array K and H</td></tr>
|
||||
<tr><td><code>$K</code></td><td>array with the quoted keys</td></tr>
|
||||
<tr><td><code>$H</code></td><td>array of integer hash values</td></tr>
|
||||
<tr><td><code>$$</code></td><td><code>$</code> (a literal dollar sign)</td></tr>
|
||||
</table>
|
||||
<p>A literal <code>$</code> is escaped as <code>$$</code>. Since the syntax for arrays is not the
|
||||
same in all programming languages, some specifics can be adjusted using
|
||||
command line options.
|
||||
The section of the built-in template which creates the actual hash function
|
||||
is:
|
||||
</p>
|
||||
<pre><code>G = [$G]
|
||||
|
||||
S1 = [$S1]
|
||||
S2 = [$S2]
|
||||
|
||||
def hash_f(key, T):
|
||||
return sum(T[i % $NS] * ord(c) for i, c in enumerate(str(key))) % $NG
|
||||
|
||||
def perfect_hash(key):
|
||||
return (G[hash_f(key, S1)] + G[hash_f(key, S2)]) % $NG
|
||||
</code></pre><p>Using code templates, makes this program very flexible. The package comes
|
||||
with several complete examples for C and C++. There are many choices one
|
||||
faces when implementing a static hash table: do the parameter lists go into
|
||||
a separate header file, should the API for the table only contain the hash
|
||||
values, but not the objects being mapped, and so on.
|
||||
All these various choices are possible because of the template is simply
|
||||
filled with the parameters, no matter what else is inside the template.
|
||||
</p>
|
||||
<p>Another possible use the program is as a python module. The functions and
|
||||
classes in <code>perfect_hash.py</code> are documented and have clean interfaces.
|
||||
The folder <code>example-Python</code> has examples which shows how the module
|
||||
can be used directly in this way.
|
||||
</p>
|
||||
|
||||
<h3>Requirement:</h3>
|
||||
<p>Python 2.5
|
||||
</p>
|
||||
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
|
||||
Generic Perfect Hash Generator
|
||||
------------------------------
|
||||
|
||||
*Ilan Schnell, 2008*
|
||||
|
||||
perfect_hash.py provides a perfect hash generator which is not language
|
||||
specific. That is, the generator can output a perfect hash function for
|
||||
a given set of keys in any programming language, this is achieved by
|
||||
filling a given code template.
|
||||
|
||||
### Acknowledgments:
|
||||
|
||||
This code is derived from A. M. Kuchling's
|
||||
[Perfect Minimal Hash Generator](http://www.amk.ca/python/code/perfect-hash).
|
||||
|
||||
### Introduction:
|
||||
|
||||
A perfect hash function of a certain set S of keys is a hash function
|
||||
which maps all keys in S to different numbers.
|
||||
That means that for the set S,
|
||||
the hash function is collision-free, or perfect.
|
||||
Further, a perfect hash function is called minimal when it maps n keys
|
||||
to n *consecutive* integers, usually in the range from 0 to n-1.
|
||||
|
||||
After coming across A. M. Kuchling's Perfect Minimal Hash Generator,
|
||||
I decided to write a general tool for generating perfect hashes.
|
||||
It is general in the sense that it can produce perfect hash functions
|
||||
for almost any programming language.
|
||||
A given code template is filled with parameters,
|
||||
such that the output is code which implements the hash function.
|
||||
|
||||
The algorithm the program uses is described in the paper
|
||||
["Optimal algorithms for minimal perfect hashing"]
|
||||
(http://citeseer.ist.psu.edu/122364.html),
|
||||
Z. J. Czech, G. Havas and B.S. Majewski.
|
||||
|
||||
I tried to illustrate the algorithm and explain how it works on
|
||||
[this page](http://ilan.schnell-web.net/prog/perfect-hash/algo.html).
|
||||
|
||||
### Usage:
|
||||
|
||||
Given a set of keys which are ordinary character string,
|
||||
the program returns a minimal perfect hash function.
|
||||
This hash function is returned in the form of Python code by default.
|
||||
Suppose we have a file with keys:
|
||||
|
||||
# 'animals.txt'
|
||||
Elephant
|
||||
Horse
|
||||
Camel
|
||||
Python
|
||||
Dog
|
||||
Cat
|
||||
|
||||
|
||||
The exact way this file is parsed can be specified using command line
|
||||
options, for example it is possible to only read one column from a file
|
||||
which contains different items in each row.
|
||||
The program is invoked like this:
|
||||
|
||||
# =======================================================================
|
||||
# ================= Python code for perfect hash function ===============
|
||||
# =======================================================================
|
||||
|
||||
G = [0, 0, 4, 1, 0, 3, 8, 1, 6]
|
||||
|
||||
S1 = [5, 0, 0, 6, 1, 0, 4, 7]
|
||||
S2 = [7, 3, 6, 7, 8, 5, 7, 6]
|
||||
|
||||
def hash_f(key, T):
|
||||
return sum(T[i % 8] * ord(c) for i, c in enumerate(str(key))) % 9
|
||||
|
||||
def perfect_hash(key):
|
||||
return (G[hash_f(key, S1)] + G[hash_f(key, S2)]) % 9
|
||||
|
||||
# ============================ Sanity check =============================
|
||||
|
||||
K = ["Elephant", "Horse", "Camel", "Python", "Dog", "Cat"]
|
||||
H = [0, 1, 2, 3, 4, 5]
|
||||
|
||||
assert len(K) == len(H) == 6
|
||||
|
||||
for k, h in zip(K, H):
|
||||
assert perfect_hash(k) == h
|
||||
|
||||
|
||||
The way the program works is by filling a code template with the calculated
|
||||
parameters. The program can take such a template in form of a file and
|
||||
fill in the calculated parameters, this allows the generation of perfect
|
||||
hash function in any programming language. The hash function is kept quite
|
||||
simple and does not require machine or language specific byte level operations
|
||||
which might be hard to implement in the target language.
|
||||
The following parameters are available in the template, and will expand to:
|
||||
|
||||
###### table
|
||||
|
||||
A literal `$` is escaped as `$$`. Since the syntax for arrays is not the
|
||||
same in all programming languages, some specifics can be adjusted using
|
||||
command line options.
|
||||
The section of the built-in template which creates the actual hash function
|
||||
is:
|
||||
|
||||
G = [$G]
|
||||
|
||||
S1 = [$S1]
|
||||
S2 = [$S2]
|
||||
|
||||
def hash_f(key, T):
|
||||
return sum(T[i % $NS] * ord(c) for i, c in enumerate(str(key))) % $NG
|
||||
|
||||
def perfect_hash(key):
|
||||
return (G[hash_f(key, S1)] + G[hash_f(key, S2)]) % $NG
|
||||
|
||||
Using code templates, makes this program very flexible. The package comes
|
||||
with several complete examples for C and C++. There are many choices one
|
||||
faces when implementing a static hash table: do the parameter lists go into
|
||||
a separate header file, should the API for the table only contain the hash
|
||||
values, but not the objects being mapped, and so on.
|
||||
All these various choices are possible because of the template is simply
|
||||
filled with the parameters, no matter what else is inside the template.
|
||||
|
||||
Another possible use the program is as a python module. The functions and
|
||||
classes in `perfect_hash.py` are documented and have clean interfaces.
|
||||
The folder `example-Python` has examples which shows how the module
|
||||
can be used directly in this way.
|
||||
|
||||
### Requirement:
|
||||
|
||||
Python 2.5
|
||||
@@ -0,0 +1,141 @@
|
||||
|
||||
Generic Perfect Hash Generator
|
||||
------------------------------
|
||||
|
||||
*Ilan Schnell, 2008*
|
||||
|
||||
perfect_hash.py provides a perfect hash generator which is not language
|
||||
specific. That is, the generator can output a perfect hash function for
|
||||
a given set of keys in any programming language, this is achieved by
|
||||
filling a given code template.
|
||||
|
||||
### Acknowledgments:
|
||||
|
||||
This code is derived from A. M. Kuchling's
|
||||
[Perfect Minimal Hash Generator](http://www.amk.ca/python/code/perfect-hash).
|
||||
|
||||
### Introduction:
|
||||
|
||||
A perfect hash function of a certain set S of keys is a hash function
|
||||
which maps all keys in S to different numbers.
|
||||
That means that for the set S,
|
||||
the hash function is collision-free, or perfect.
|
||||
Further, a perfect hash function is called minimal when it maps n keys
|
||||
to n *consecutive* integers, usually in the range from 0 to n-1.
|
||||
|
||||
After coming across A. M. Kuchling's Perfect Minimal Hash Generator,
|
||||
I decided to write a general tool for generating perfect hashes.
|
||||
It is general in the sense that it can produce perfect hash functions
|
||||
for almost any programming language.
|
||||
A given code template is filled with parameters,
|
||||
such that the output is code which implements the hash function.
|
||||
|
||||
The algorithm the program uses is described in the paper
|
||||
["Optimal algorithms for minimal perfect hashing"]
|
||||
(http://citeseer.ist.psu.edu/122364.html),
|
||||
Z. J. Czech, G. Havas and B.S. Majewski.
|
||||
|
||||
I tried to illustrate the algorithm and explain how it works on
|
||||
[this page](http://ilan.schnell-web.net/prog/perfect-hash/algo.html).
|
||||
|
||||
### Usage:
|
||||
|
||||
Given a set of keys which are ordinary character string,
|
||||
the program returns a minimal perfect hash function.
|
||||
This hash function is returned in the form of Python code by default.
|
||||
Suppose we have a file with keys:
|
||||
|
||||
# 'animals.txt'
|
||||
Elephant
|
||||
Horse
|
||||
Camel
|
||||
Python
|
||||
Dog
|
||||
Cat
|
||||
|
||||
|
||||
The exact way this file is parsed can be specified using command line
|
||||
options, for example it is possible to only read one column from a file
|
||||
which contains different items in each row.
|
||||
The program is invoked like this:
|
||||
|
||||
# =======================================================================
|
||||
# ================= Python code for perfect hash function ===============
|
||||
# =======================================================================
|
||||
|
||||
G = [0, 0, 4, 1, 0, 3, 8, 1, 6]
|
||||
|
||||
S1 = [5, 0, 0, 6, 1, 0, 4, 7]
|
||||
S2 = [7, 3, 6, 7, 8, 5, 7, 6]
|
||||
|
||||
def hash_f(key, T):
|
||||
return sum(T[i % 8] * ord(c) for i, c in enumerate(str(key))) % 9
|
||||
|
||||
def perfect_hash(key):
|
||||
return (G[hash_f(key, S1)] + G[hash_f(key, S2)]) % 9
|
||||
|
||||
# ============================ Sanity check =============================
|
||||
|
||||
K = ["Elephant", "Horse", "Camel", "Python", "Dog", "Cat"]
|
||||
H = [0, 1, 2, 3, 4, 5]
|
||||
|
||||
assert len(K) == len(H) == 6
|
||||
|
||||
for k, h in zip(K, H):
|
||||
assert perfect_hash(k) == h
|
||||
|
||||
|
||||
The way the program works is by filling a code template with the calculated
|
||||
parameters. The program can take such a template in form of a file and
|
||||
fill in the calculated parameters, this allows the generation of perfect
|
||||
hash function in any programming language. The hash function is kept quite
|
||||
simple and does not require machine or language specific byte level operations
|
||||
which might be hard to implement in the target language.
|
||||
The following parameters are available in the template, and will expand to:
|
||||
|
||||
string | expands to
|
||||
--------+--------------------------------
|
||||
$NS | the length of S1 and S2
|
||||
$S1 | array of integers S1
|
||||
$S2 | array of integers S2
|
||||
$NG | length of array G
|
||||
$G | array of integers G
|
||||
$NK | the number of keys, i.e. length of array K and H
|
||||
$K | array with the quoted keys
|
||||
$H | array of integer hash values
|
||||
$$ | $ (a literal dollar sign)
|
||||
|
||||
|
||||
A literal '$' is escaped as '$$'. Since the syntax for arrays is not the
|
||||
same in all programming languages, some specifics can be adjusted using
|
||||
command line options.
|
||||
The section of the built-in template which creates the actual hash function
|
||||
is:
|
||||
|
||||
G = [$G]
|
||||
|
||||
S1 = [$S1]
|
||||
S2 = [$S2]
|
||||
|
||||
def hash_f(key, T):
|
||||
return sum(T[i % $NS] * ord(c) for i, c in enumerate(str(key))) % $NG
|
||||
|
||||
def perfect_hash(key):
|
||||
return (G[hash_f(key, S1)] + G[hash_f(key, S2)]) % $NG
|
||||
|
||||
Using code templates, makes this program very flexible. The package comes
|
||||
with several complete examples for C and C++. There are many choices one
|
||||
faces when implementing a static hash table: do the parameter lists go into
|
||||
a separate header file, should the API for the table only contain the hash
|
||||
values, but not the objects being mapped, and so on.
|
||||
All these various choices are possible because of the template is simply
|
||||
filled with the parameters, no matter what else is inside the template.
|
||||
|
||||
Another possible use the program is as a python module. The functions and
|
||||
classes in 'perfect_hash.py' are documented and have clean interfaces.
|
||||
The folder 'example-Python' has examples which shows how the module
|
||||
can be used directly in this way.
|
||||
|
||||
### Requirement:
|
||||
|
||||
Python 2.5
|
||||
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env python
|
||||
import re
|
||||
|
||||
def convert(line, head = False):
|
||||
pat = re.compile(r'([$]\S*)')
|
||||
line = pat.sub(r'<code>\1</code>', line)
|
||||
|
||||
row = [x.strip() for x in line.split('|')]
|
||||
fmt = ' <tr><td>%s</td><td>%s</td></tr>'
|
||||
if head:
|
||||
fmt = fmt.replace('td', 'th')
|
||||
print fmt % tuple(row)
|
||||
|
||||
|
||||
f = file('parameters.txt')
|
||||
|
||||
print '<table>'
|
||||
convert(f.readline(), head = True)
|
||||
f.readline()
|
||||
for line in f:
|
||||
convert(line)
|
||||
print '</table>'
|
||||
@@ -0,0 +1,11 @@
|
||||
string | expands to
|
||||
--------+--------------------------------
|
||||
$NS | the length of S1 and S2
|
||||
$S1 | array of integers S1
|
||||
$S2 | array of integers S2
|
||||
$NG | length of array G
|
||||
$G | array of integers G
|
||||
$NK | the number of keys, i.e. length of array K and H
|
||||
$K | array with the quoted keys
|
||||
$H | array of integer hash values
|
||||
$$ | $ (a literal dollar sign)
|
||||
@@ -0,0 +1,31 @@
|
||||
|
||||
CXX = g++ -Wall
|
||||
|
||||
|
||||
lookup: main.o states-code.o
|
||||
$(CXX) -o $@ $^
|
||||
|
||||
|
||||
main.o: main.cc states-code.hh
|
||||
$(CXX) -c $<
|
||||
|
||||
|
||||
states-code.o: states-code.cc states-code.hh states.dat.h
|
||||
$(CXX) -c $<
|
||||
|
||||
|
||||
states-code.cc: states.dat states-tmpl.cc states-code.hh
|
||||
../perfect_hash.py --splitby '|' --keycol 2 states.dat states-tmpl.cc
|
||||
|
||||
|
||||
states.dat.h: states.dat
|
||||
./mk_header.py >$@
|
||||
|
||||
|
||||
clean:
|
||||
rm lookup *.o states.dat.h states-code.cc
|
||||
|
||||
|
||||
test:
|
||||
./lookup 'NY'
|
||||
./lookup 'QW'
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
using namespace std;
|
||||
|
||||
#include "states-code.hh"
|
||||
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
if (argc != 2) {
|
||||
printf ("Usage: %s <abbreviation>\n", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
string abbr = argv[1];
|
||||
|
||||
State s(abbr);
|
||||
|
||||
cout << "The state of " << s.name ()
|
||||
<< " has a population of " << 1e-6 * s.population () << " million.\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
for line in file('states.dat'):
|
||||
line = line.strip()
|
||||
if line.startswith('#'):
|
||||
continue
|
||||
|
||||
row = tuple(entry.strip() for entry in line.split('|'))
|
||||
|
||||
print ' { "%s", "%s", %s },' % row
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
|
||||
#include <string>
|
||||
using namespace std;
|
||||
|
||||
|
||||
class State
|
||||
{
|
||||
public:
|
||||
State (const string abbr);
|
||||
|
||||
string name () const { return nam; };
|
||||
int population () const { return pop; };
|
||||
|
||||
private:
|
||||
string nam;
|
||||
int pop;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "states-code.hh"
|
||||
|
||||
static struct {
|
||||
char *name;
|
||||
char *abbr;
|
||||
int pop;
|
||||
} states[$NK] = {
|
||||
#include "states.dat.h"
|
||||
};
|
||||
|
||||
static int T1[] = { $S1 };
|
||||
|
||||
static int T2[] = { $S2 };
|
||||
|
||||
static int G[] = { $G };
|
||||
|
||||
static int hash_g (const char *key, const int *T)
|
||||
{
|
||||
int i, sum = 0;
|
||||
|
||||
for (i = 0; key[i] != '\0'; i++) {
|
||||
sum += T[i] * key[i];
|
||||
sum %= $NG;
|
||||
}
|
||||
return G[sum];
|
||||
}
|
||||
|
||||
static int perfect_hash (const char *key)
|
||||
{
|
||||
if (strlen (key) > $NS)
|
||||
return 0;
|
||||
|
||||
return (hash_g (key, T1) + hash_g (key, T2)) % $NG;
|
||||
}
|
||||
|
||||
State::State (const string abbr)
|
||||
{
|
||||
int hash_value = perfect_hash (abbr.c_str ());
|
||||
|
||||
if (hash_value < $NK &&
|
||||
strcmp(abbr.c_str (), states[hash_value].abbr) == 0)
|
||||
{
|
||||
nam = states[hash_value].name;
|
||||
pop = states[hash_value].pop;
|
||||
}
|
||||
else
|
||||
{
|
||||
cerr << "'" << abbr << "' is not an abbreviation for a state\n";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
# Name | Abr | Population
|
||||
#--------------+------+---------
|
||||
Alabama | AL | 4335400
|
||||
Alaska | AK | 611500
|
||||
Arizona | AZ | 4664600
|
||||
Arkansas | AR | 2531000
|
||||
California | CA | 33198100
|
||||
Colorado | CO | 3930700
|
||||
Connecticut | CT | 3271100
|
||||
Delaware | DE | 736900
|
||||
Florida | FL | 15012200
|
||||
Georgia | GA | 7562200
|
||||
Hawaii | HI | 1188400
|
||||
Idaho | ID | 1221500
|
||||
Illinois | IL | 11981700
|
||||
Indiana | IN | 5882500
|
||||
Iowa | IA | 2854700
|
||||
Kansas | KS | 2603200
|
||||
Kentucky | KY | 3921000
|
||||
Louisiana | LA | 4361200
|
||||
Maine | ME | 1243700
|
||||
Maryland | MD | 5122400
|
||||
Massachusetts | MA | 6133500
|
||||
Michigan | MI | 9825100
|
||||
Minnesota | MN | 4704200
|
||||
Mississippi | MS | 2739700
|
||||
Missouri | MO | 5421400
|
||||
Montana | MT | 886400
|
||||
Nebraska | NE | 1661400
|
||||
Nevada | NV | 1828700
|
||||
New Hampshire | NH | 1179100
|
||||
New Jersey | NJ | 8078300
|
||||
New Mexico | NM | 1738700
|
||||
New York | NY | 18197800
|
||||
North Carolina | NC | 7483100
|
||||
North Dakota | ND | 640000
|
||||
Ohio | OH | 11197900
|
||||
Oklahoma | OK | 3328100
|
||||
Oregon | OR | 3266800
|
||||
Pennsylvania | PA | 12044200
|
||||
Rhode Island | RI | 987000
|
||||
South Carolina | SC | 3781800
|
||||
South Dakota | SD | 738500
|
||||
Tennessee | TN | 5398200
|
||||
Texas | TX | 19274300
|
||||
Utah | UT | 2071500
|
||||
Vermont | VT | 590400
|
||||
Virginia | VA | 6768400
|
||||
Washington | WA | 5674900
|
||||
West Virginia | WV | 1813200
|
||||
Wisconsin | WI | 5224500
|
||||
Wyoming | WY | 479500
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
|
||||
CC = gcc -Wall
|
||||
|
||||
|
||||
stations.so: stationsmodule.c stations.dat.h stations-code.h
|
||||
$(CC) -shared -fPIC -I/usr/local/include/python2.5 \
|
||||
-o stations.so stationsmodule.c
|
||||
|
||||
|
||||
stations.dat.h: stations.dat
|
||||
sed <$< >$@ -e 's:\([^,]*\),\([^,]*\): { "\1", "\2" },:'
|
||||
|
||||
|
||||
stations-code.h: stations.dat stations-tmpl.h
|
||||
../perfect_hash.py --trails 2 $^
|
||||
|
||||
|
||||
clean:
|
||||
rm stations-code.h stations.dat.h stations.so
|
||||
|
||||
|
||||
test:
|
||||
python -c "import stations; print stations.locator('DL5BAC')"
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
#define NK $NK /* number of keys */
|
||||
#define NG $NG /* number of vertices */
|
||||
#define NS $NS /* elements in T */
|
||||
|
||||
|
||||
int G[] = { $G };
|
||||
|
||||
int T1[] = { $S1 };
|
||||
|
||||
int T2[] = { $S2 };
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,69 @@
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "stations-code.h"
|
||||
|
||||
static struct {
|
||||
char *callsign;
|
||||
char *locator;
|
||||
} station_list[] = {
|
||||
#include "stations.dat.h"
|
||||
};
|
||||
|
||||
static int hash_f (const char *s, const int *T)
|
||||
{
|
||||
register int i, sum = 0;
|
||||
|
||||
for (i = 0; s[i] != '\0'; i++) {
|
||||
sum += T[i] * s[i];
|
||||
sum %= NG;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
static int perf_hash (const char *k)
|
||||
{
|
||||
if (strlen (k) > NS)
|
||||
return 0;
|
||||
|
||||
return (G[ hash_f(k, T1) ] + G[ hash_f(k, T2)] ) % NG;
|
||||
}
|
||||
|
||||
static int getlocator (char *locator, const char *callsign)
|
||||
{
|
||||
int hashval = perf_hash (callsign);
|
||||
|
||||
if (hashval < NK && strcmp(callsign, station_list[hashval].callsign) == 0) {
|
||||
strcpy (locator, station_list[hashval].locator);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
stations_locator(PyObject *self, PyObject *args)
|
||||
{
|
||||
const char *callsign;
|
||||
char locator[6];
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s", &callsign))
|
||||
return NULL;
|
||||
|
||||
return Py_BuildValue("s", (getlocator (locator, callsign) == 1) ?
|
||||
locator : NULL);
|
||||
}
|
||||
|
||||
static PyMethodDef StationsMethods[] = {
|
||||
|
||||
{"locator", stations_locator, METH_VARARGS,
|
||||
"Get locator from callsign."},
|
||||
|
||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
|
||||
PyMODINIT_FUNC
|
||||
initstations(void)
|
||||
{
|
||||
(void) Py_InitModule("stations", StationsMethods);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env python
|
||||
import sys
|
||||
from timeit import Timer
|
||||
from stations import locator
|
||||
|
||||
call = sys.argv[1]
|
||||
print repr(call)
|
||||
D = {}
|
||||
|
||||
for line in file('stations.dat'):
|
||||
c, l = [x.strip() for x in line.split(',')]
|
||||
D[c] = l
|
||||
|
||||
def test1(c):
|
||||
return D[c]
|
||||
|
||||
print repr(test1(call))
|
||||
t = Timer("test1(%r)" % call, "from __main__ import test1")
|
||||
print t.timeit()
|
||||
|
||||
# -----
|
||||
|
||||
def test2(c):
|
||||
return locator(c)
|
||||
|
||||
print repr(test2(call))
|
||||
t = Timer("test2(%r)" % call, "from __main__ import test2")
|
||||
print t.timeit()
|
||||
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
This example shows how to use the class Graph.
|
||||
|
||||
The class implements a graph with 'N' vertices. First, you connect the
|
||||
graph with edges, which have a desired value associated. Then the vertex
|
||||
values are assigned, which will fail if the graph is cyclic. The vertex
|
||||
values are assigned such that the two values corresponding to an edge add
|
||||
up to the desired edge value (mod N).
|
||||
"""
|
||||
import sys
|
||||
|
||||
sys.path.append('..')
|
||||
from perfect_hash import Graph
|
||||
|
||||
G = Graph(3)
|
||||
assert G.assign_vertex_values() == True
|
||||
|
||||
# Now we make an edge between vertex 0 and 1 with desired edge value 2:
|
||||
G.connect(0, 1, 2)
|
||||
|
||||
# Make another edge 1:2 with desired edge value 1:
|
||||
G.connect(1, 2, 1)
|
||||
|
||||
# The graph is still acyclic, and assigning values works:
|
||||
assert G.assign_vertex_values() == True
|
||||
assert G.vertex_values == [0, 2, 2]
|
||||
|
||||
# What do these values mean?
|
||||
# When you add the values for edge 0:1 you get 0 + 2 = 2, as desired.
|
||||
# For edge 1:2 you add 2 + 2 = 4 = 1 (mod 3), as desired.
|
||||
|
||||
# Adding edge 0:2 produces a loop, so the graph is no longer acyclic.
|
||||
# Assigning values fails.
|
||||
G.connect(0, 2, 0)
|
||||
|
||||
assert G.assign_vertex_values() == False
|
||||
|
||||
|
||||
print 'OK'
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
|
||||
all:
|
||||
true
|
||||
|
||||
|
||||
clean:
|
||||
true
|
||||
|
||||
|
||||
test:
|
||||
./generate_hash.py
|
||||
./PerfHash.py
|
||||
./Graph.py
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
This example shows how to use the class PerfHash.
|
||||
|
||||
This class is designed for creating perfect hash tables at run time,
|
||||
which should be avoided, in particulat inserting new keys is
|
||||
prohibitively expensive since a new perfect hash table needs to be
|
||||
constructed. However, this class can be usefull for testing.
|
||||
|
||||
For practical programming purposes in Python the class PerfHash
|
||||
should never be used because Python's built-in dictionary is very
|
||||
efficient and always faster than PerfHash.
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
sys.path.append('..')
|
||||
from perfect_hash import PerfHash
|
||||
|
||||
|
||||
month = dict(zip('jan feb mar apr may jun jul aug sep oct mov dec'.split(),
|
||||
range(1, 13)))
|
||||
|
||||
d = PerfHash(month)
|
||||
|
||||
|
||||
for m in month:
|
||||
assert month[m] == d[m]
|
||||
|
||||
d[True] = False
|
||||
|
||||
assert d[True] == False
|
||||
|
||||
for i in xrange(10): # very expensive
|
||||
d[i] = 2*i*i + 3*i -7
|
||||
|
||||
assert d[4] == 37
|
||||
|
||||
|
||||
print 'OK'
|
||||
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
This example shows how to use the function generate_hash.
|
||||
|
||||
generate_hash(kdic, Hash)
|
||||
|
||||
returns hash functions f1 and f2, and G for a perfect minimal hash.
|
||||
Input is dictionary 'kdic' with the keys and desired hash values.
|
||||
'Hash' is a random hash function generator, that means Hash(N) returns a
|
||||
returns a random hash function which returns hash values from 0..N-1.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import random, string
|
||||
|
||||
sys.path.append('..')
|
||||
from perfect_hash import generate_hash
|
||||
|
||||
|
||||
|
||||
month = dict(zip('jan feb mar apr may jun jul aug sep oct mov dec'.split(),
|
||||
range(1, 13)))
|
||||
|
||||
def mkRandHash(N):
|
||||
"""
|
||||
Return a random hash function which returns hash values from 0..N-1.
|
||||
"""
|
||||
junk = "".join(random.choice(string.letters + string.digits)
|
||||
for i in xrange(10))
|
||||
return lambda key: hash(junk + str(key)) % N
|
||||
|
||||
|
||||
f1, f2, G = generate_hash(month, mkRandHash)
|
||||
|
||||
for k, h in month.items():
|
||||
assert h == ( G[f1(k)] + G[f2(k)] ) % len(G)
|
||||
|
||||
print 'OK'
|
||||
@@ -0,0 +1,31 @@
|
||||
|
||||
CC = gcc -Wall
|
||||
|
||||
|
||||
lookup: main.o states-code.o
|
||||
$(CC) -o $@ $^
|
||||
|
||||
|
||||
main.o: main.c states-code.h states.dat.h
|
||||
$(CC) -c $<
|
||||
|
||||
|
||||
states-code.o: states-code.c states-code.h
|
||||
$(CC) -c $<
|
||||
|
||||
|
||||
states-code.c: states.dat states-tmpl.c
|
||||
../perfect_hash.py -vvvv --splitby '|' --keycol 2 $^
|
||||
|
||||
|
||||
states.dat.h: states.dat
|
||||
./mk_header.py >$@
|
||||
|
||||
|
||||
clean:
|
||||
rm lookup *.o states.dat.h states-code.c
|
||||
|
||||
|
||||
test:
|
||||
./lookup 'NY'
|
||||
./lookup 'QW'
|
||||
@@ -0,0 +1,33 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "states-code.h"
|
||||
|
||||
struct {
|
||||
char *name;
|
||||
char *abbr;
|
||||
int pop;
|
||||
} states[] = {
|
||||
#include "states.dat.h"
|
||||
};
|
||||
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
if (argc != 2) {
|
||||
printf ("Usage: %s <abbreviation>\n", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
char *abbr = argv[1];
|
||||
int hashval;
|
||||
|
||||
if ((hashval = has_key(abbr)) == -1)
|
||||
printf ("'%s' is not an abbreviation for a state.\n", abbr);
|
||||
else
|
||||
printf ("The state of %s has a population of %g million.\n",
|
||||
states[hashval].name,
|
||||
1e-6 * states[hashval].pop);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
for line in file('states.dat'):
|
||||
line = line.strip()
|
||||
if line.startswith('#'):
|
||||
continue
|
||||
|
||||
row = tuple(entry.strip() for entry in line.split('|'))
|
||||
|
||||
print ' { "%s", "%s", %s },' % row
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
|
||||
/* Return hash value of abbreviation 'abbr' if found, -1 otherwise */
|
||||
int has_key (const char *abbr);
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "states-code.h"
|
||||
|
||||
|
||||
static int T1[] = { $S1 };
|
||||
|
||||
static int T2[] = { $S2 };
|
||||
|
||||
static int G[] = { $G };
|
||||
|
||||
static char *K[] = { $K };
|
||||
|
||||
static int hash_g (const char *key, const int *T)
|
||||
{
|
||||
int i, sum = 0;
|
||||
|
||||
for (i = 0; key[i] != '\0'; i++) {
|
||||
sum += T[i] * key[i];
|
||||
sum %= $NG;
|
||||
}
|
||||
return G[sum];
|
||||
}
|
||||
|
||||
static int perfect_hash (const char *key)
|
||||
{
|
||||
if (strlen (key) > $NS)
|
||||
return 0;
|
||||
|
||||
return (hash_g (key, T1) + hash_g (key, T2)) % $NG;
|
||||
}
|
||||
|
||||
int has_key (const char *abbr)
|
||||
{
|
||||
int hash_value = perfect_hash (abbr);
|
||||
|
||||
if (hash_value < $NK && strcmp(abbr, K[hash_value]) == 0)
|
||||
return hash_value;
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
# Name | Abr | Population
|
||||
#--------------+------+---------
|
||||
Alabama | AL | 4335400
|
||||
Alaska | AK | 611500
|
||||
Arizona | AZ | 4664600
|
||||
Arkansas | AR | 2531000
|
||||
California | CA | 33198100
|
||||
Colorado | CO | 3930700
|
||||
Connecticut | CT | 3271100
|
||||
Delaware | DE | 736900
|
||||
Florida | FL | 15012200
|
||||
Georgia | GA | 7562200
|
||||
Hawaii | HI | 1188400
|
||||
Idaho | ID | 1221500
|
||||
Illinois | IL | 11981700
|
||||
Indiana | IN | 5882500
|
||||
Iowa | IA | 2854700
|
||||
Kansas | KS | 2603200
|
||||
Kentucky | KY | 3921000
|
||||
Louisiana | LA | 4361200
|
||||
Maine | ME | 1243700
|
||||
Maryland | MD | 5122400
|
||||
Massachusetts | MA | 6133500
|
||||
Michigan | MI | 9825100
|
||||
Minnesota | MN | 4704200
|
||||
Mississippi | MS | 2739700
|
||||
Missouri | MO | 5421400
|
||||
Montana | MT | 886400
|
||||
Nebraska | NE | 1661400
|
||||
Nevada | NV | 1828700
|
||||
New Hampshire | NH | 1179100
|
||||
New Jersey | NJ | 8078300
|
||||
New Mexico | NM | 1738700
|
||||
New York | NY | 18197800
|
||||
North Carolina | NC | 7483100
|
||||
North Dakota | ND | 640000
|
||||
Ohio | OH | 11197900
|
||||
Oklahoma | OK | 3328100
|
||||
Oregon | OR | 3266800
|
||||
Pennsylvania | PA | 12044200
|
||||
Rhode Island | RI | 987000
|
||||
South Carolina | SC | 3781800
|
||||
South Dakota | SD | 738500
|
||||
Tennessee | TN | 5398200
|
||||
Texas | TX | 19274300
|
||||
Utah | UT | 2071500
|
||||
Vermont | VT | 590400
|
||||
Virginia | VA | 6768400
|
||||
Washington | WA | 5674900
|
||||
West Virginia | WV | 1813200
|
||||
Wisconsin | WI | 5224500
|
||||
Wyoming | WY | 479500
|
||||
@@ -0,0 +1,22 @@
|
||||
|
||||
CC = gcc -Wall
|
||||
|
||||
a.out: main.c keys.code.h
|
||||
$(CC) $<
|
||||
|
||||
|
||||
keys.code.h: keys.dat keys.tmpl.h
|
||||
../perfect_hash.py $^
|
||||
|
||||
|
||||
keys.dat:
|
||||
./mk_rnd_keys.py 100 >keys.dat
|
||||
|
||||
|
||||
clean:
|
||||
rm keys.dat keys.code.h a.out
|
||||
|
||||
|
||||
test:
|
||||
./a.out
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
#define NK $NK /* number of keys */
|
||||
#define NG $NG /* number of vertices */
|
||||
#define NS $NS /* length of array T1 and T2 */
|
||||
|
||||
int T1[] = { $S1 };
|
||||
|
||||
int T2[] = { $S2 };
|
||||
|
||||
int G[] = { $G };
|
||||
|
||||
char *K[] = { $K };
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "keys.code.h"
|
||||
|
||||
int hash_g (char *s, int *T)
|
||||
{
|
||||
int i, f = 0;
|
||||
|
||||
for (i = 0; s[i] != '\0'; i++) {
|
||||
f += T[i] * s[i];
|
||||
f %= NG;
|
||||
}
|
||||
return G[f];
|
||||
}
|
||||
|
||||
int hash (char *k)
|
||||
{
|
||||
if (strlen (k) > NS)
|
||||
return 0;
|
||||
|
||||
return (hash_g (k, T1) + hash_g (k, T2)) % NG;
|
||||
}
|
||||
|
||||
bool has_key (char *k)
|
||||
{
|
||||
int h = hash (k);
|
||||
|
||||
return h < NK && strcmp(k, K[h]) == 0;
|
||||
}
|
||||
|
||||
int main ()
|
||||
{
|
||||
int i;
|
||||
|
||||
char *junk = "acnhuvn5yushvghnw7og5siuhgsiuhnglsh45vgghwn";
|
||||
|
||||
assert (has_key(junk) == 0);
|
||||
assert (hash(junk) == 0);
|
||||
|
||||
for (i = 0; i < NK; i++) {
|
||||
assert (has_key(K[i]) == true);
|
||||
assert (hash(K[i]) == i);
|
||||
}
|
||||
puts ("OK");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# ./mk_rnd_keys.py 10000 | sort | uniq | shuf >keywords.txt
|
||||
|
||||
import sys
|
||||
from random import choice, randint
|
||||
from string import digits, uppercase, lowercase
|
||||
|
||||
def key():
|
||||
return ''.join(choice(uppercase + lowercase + digits)
|
||||
for i in xrange(randint(6, 20)))
|
||||
|
||||
N = int(sys.argv[1])
|
||||
|
||||
|
||||
for n in xrange(N):
|
||||
print key()
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
|
||||
|
||||
animals.ps: animals.dot
|
||||
neato -Tps $< -Gstart=100 -o $@
|
||||
|
||||
|
||||
animals.dot: animals.py
|
||||
./py2dot -l $< -o $@
|
||||
|
||||
|
||||
animals.py: ../animals.txt
|
||||
../perfect_hash.py $< >$@
|
||||
|
||||
|
||||
clean:
|
||||
rm animals.py animals.dot animals.ps
|
||||
|
||||
|
||||
test:
|
||||
true
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
../perfect_hash.py ../animals.txt | ./py2dot | neato -Tps -Gstart=100 -o out.ps
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
#!/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:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
|
||||
function showpwd ()
|
||||
{
|
||||
echo '=============' `pwd`
|
||||
}
|
||||
|
||||
# perform programs self test
|
||||
showpwd
|
||||
./perfect_hash.py --test || exit 1
|
||||
|
||||
# update documentation
|
||||
for folder in doc
|
||||
do
|
||||
cd $folder
|
||||
showpwd
|
||||
make
|
||||
cd ..
|
||||
done
|
||||
|
||||
# run examples
|
||||
for folder in example* graph
|
||||
do
|
||||
cd $folder
|
||||
showpwd
|
||||
make || exit 1
|
||||
make test || exit 1
|
||||
make clean
|
||||
cd ..
|
||||
done
|
||||
|
||||
rm perfect_hash.pyc
|
||||
@@ -0,0 +1,163 @@
|
||||
#encoding: utf-8
|
||||
from perfect_hash import perfect_hash
|
||||
|
||||
import re, string, os, random
|
||||
|
||||
cur_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
unicode_chinese_re = u'[\u2E80-\u2EFF\u2F00-\u2FDF\u3000-\u303F\u31C0-\u31EF\u3200-\u32FF\u3300-\u33FF\u3400-\u4DBF\u4DC0-\u4DFF\u4E00-\u9FBF\uF900-\uFAFF\uFE30-\uFE4F\uFF00-\uFFEF]'
|
||||
match_re = re.compile(unicode_chinese_re)
|
||||
|
||||
def _get_font_lib(f):
|
||||
reading_data = False
|
||||
data = []
|
||||
for i in f.readlines():
|
||||
if i.strip() == 'FONT_BMP_DATA_BEGIN':
|
||||
reading_data = True
|
||||
continue
|
||||
if i.strip() == 'FONT_BMP_DATA_END':
|
||||
break
|
||||
if reading_data:
|
||||
line = [k for k in i.strip().split(',') if k]
|
||||
data.extend([int(k, 16) for k in line])
|
||||
return data
|
||||
|
||||
class font_lib(object):
|
||||
def __init__(self, f, width, height, encoding):
|
||||
self.width = width
|
||||
self.height = height
|
||||
self._lib = _get_font_lib(f)
|
||||
# byte per charactor
|
||||
self._bpc = (width+7)//8*height
|
||||
self.encoding = encoding
|
||||
self._finished_push = False
|
||||
|
||||
self.char_dict = {}
|
||||
|
||||
def get_char_data(self, char):
|
||||
#char_gb = char.encode(self.encoding)
|
||||
|
||||
# copied from font_hz_bmp.c
|
||||
sec, idx = [ord(i) - 0xA0 for i in char]
|
||||
#print 'sec %d, idx %d for' % (sec, idx), char
|
||||
start = (94 * (sec-1) + (idx-1)) * self._bpc
|
||||
return self._lib[start:start+self._bpc]
|
||||
|
||||
def push_char(self, c):
|
||||
self.char_dict[c] = self.char_dict.get(c, 0) + 1
|
||||
|
||||
def push_file(self, f):
|
||||
try:
|
||||
for i in f:
|
||||
t = re.findall(match_re, unicode(i.decode(self.encoding)))
|
||||
if t:
|
||||
for c in t:
|
||||
self.push_char(c.encode(self.encoding))
|
||||
except UnicodeDecodeError as e:
|
||||
try:
|
||||
print 'error in decoding %s' % f.name
|
||||
except:
|
||||
print 'error in decoding string %s' % f
|
||||
# re-raise the exception and terminate the building process
|
||||
raise
|
||||
|
||||
def _finish_push(self):
|
||||
if self._finished_push:
|
||||
return
|
||||
|
||||
self._char_li = zip(self.char_dict.keys(), self.char_dict.values())
|
||||
self._char_li.sort(key=lambda x:x[1], reverse=True)
|
||||
self._finished_push = True
|
||||
|
||||
#for i in self._char_li:
|
||||
#print i[0], i[1]
|
||||
|
||||
def get_hash_map(self):
|
||||
self._finish_push()
|
||||
li = []
|
||||
for i, k in enumerate(self._char_li):
|
||||
li.append((k[0], i))
|
||||
return li
|
||||
|
||||
def get_new_font_lib(self):
|
||||
self._finish_push()
|
||||
dat = []
|
||||
for c, f in self._char_li:
|
||||
dat.extend(self.get_char_data(c))
|
||||
return dat
|
||||
|
||||
def finish(self):
|
||||
return self.get_hash_map(), self.get_new_font_lib()
|
||||
|
||||
class mph_options(object):
|
||||
'mock object for options'
|
||||
def __init__(self, verbose=4, delimiter=', ', indent=4, width=80):
|
||||
self.verbose = verbose
|
||||
self.delimiter = delimiter
|
||||
self.indent = indent
|
||||
self.width = width
|
||||
|
||||
def gen_char_mph(font_lib):
|
||||
template = open(os.path.join(cur_dir, '..', 'common', 'font_mph-tmpl.c'), 'r').read()
|
||||
opt = mph_options()
|
||||
hmap, flib = font_lib.finish()
|
||||
#print 'compact font lib: %d chars included.' % len(hmap)
|
||||
#for i in hmap:
|
||||
#print i[0], repr(i[0]), i[1]
|
||||
code = perfect_hash.generate_code(hmap, template, perfect_hash.Hash2, opt,
|
||||
extra_subs={
|
||||
'width':str(font_lib.width),
|
||||
'height':str(font_lib.height),
|
||||
'font_data':', '.join([hex(i) for i in flib])})
|
||||
|
||||
return code
|
||||
|
||||
# {name:[file_name, height, width, encoding, instance]}
|
||||
_font_map = {'hz16':{'fname':'common/hz16font.c',
|
||||
'height':16,
|
||||
'width':16,
|
||||
'encoding':'GB2312',
|
||||
'flib':None},
|
||||
'hz12':{'fname':'common/hz12font.c',
|
||||
'height':12,
|
||||
'width':12,
|
||||
'encoding':'GB2312',
|
||||
'flib':None}
|
||||
}
|
||||
|
||||
def get_font_lib(name):
|
||||
if name not in _font_map.keys():
|
||||
return None
|
||||
|
||||
if _font_map[name]['flib'] is None:
|
||||
_font_map[name]['flib'] = font_lib(open(
|
||||
os.path.join(cur_dir, '..', _font_map[name]['fname']), 'r'),
|
||||
_font_map[name]['height'],
|
||||
_font_map[name]['width'],
|
||||
_font_map[name]['encoding'])
|
||||
return _font_map[name]['flib']
|
||||
|
||||
def gen_cmp_font_file():
|
||||
for i in _font_map:
|
||||
fl = _font_map[i]['flib']
|
||||
if fl is not None:
|
||||
code = gen_char_mph(fl)
|
||||
with open(os.path.join(cur_dir, '..', 'common', 'font_cmp_%s.c' % i), 'w') as f:
|
||||
f.write(code)
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
|
||||
lib = get_font_lib('hz16')
|
||||
libn = get_font_lib('hz16')
|
||||
assert(lib is libn)
|
||||
|
||||
lib.push_file(open(sys.argv[1], 'rb'))
|
||||
|
||||
hmap, flib = lib.finish()
|
||||
for i in hmap:
|
||||
print i[0], i[1]
|
||||
|
||||
assert(len(flib) == 32 * len(hmap))
|
||||
|
||||
print gen_char_mph(lib)
|
||||
Reference in New Issue
Block a user