sync with rtgui-0.6

This commit is contained in:
Grissiom
2013-02-01 10:27:10 +08:00
parent 4f02e67bef
commit f06c70feed
109 changed files with 6724 additions and 1164 deletions
@@ -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
@@ -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">&quot;Optimal algorithms for minimal perfect hashing&quot;</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 = [&quot;Elephant&quot;, &quot;Horse&quot;, &quot;Camel&quot;, &quot;Python&quot;, &quot;Dog&quot;, &quot;Cat&quot;]
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
+32
View File
@@ -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
+163
View File
@@ -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)